1.JavaScript
规定了几种语言类型
JS包括7种数据类型,其中有分为原始类型和引用类型。
原始类型
-
Null
: 只包含一个值null
-
Undefined
: 只包含一个值undrfined
-
Number
: 整数和浮点数,还有一些特殊值(-Infinity
、+Infinity
和NaN
) -
Boolean
: 包含两个值true
和false
-
String
: 一串表示文本值的字符序列 -
Symbol
: 一种实例是唯一且不可改变的数据类型
引用类型
-
Object
、Array
、Function
2.JavaScript
对象的底层数据结构是什么
- 原始类型直接存储在栈中,每种类型数据占用的内存空间大小时确定的,并由系统自动分配和自动释放;
- 引用类型会被存储在堆中,准确来说,引用类型的值实际存储在堆内存中,他在栈中只存储了一个固定长度的地址,这个地址指向堆内存的值。
- 数据在内存中的存储结构,也叫物理结构,分为两种:顺序存储结构和链式存储结构:
- 顺序存储结构 : 是把数据元素存放在地址连续的存储单元里,其数据见的逻辑关系和物理关系是一致的。数组就是其中的代表。
- 链式存储结构:是把数据元素存放在任意的存储单元里,这些数据在内存中的位置有可能是连续的,也有可能不不连续的,链表就是其中的代表。
- 形象地说,就是去银行取钱,顺序存储结构就是客户按照先来后到坐在标有顺序的座椅上,等候办理业务;链式存储结构就是客户拿着叫号机给的编号,然后随意地坐在没有顺序的座椅上,等候叫号办理业务。
3.null
和undefined
的区别
-
null
: 表示被赋值过的对象,刻意把一个对象赋值为null
,表示该值为空,不应该有值。转换数值时的值为0
; -
undefined
: 表示缺少值,在对象初始化的时候未进行赋值定义。转换数值时的值为NaN
4.至少可以说出三种判断JavaScript
数据类型的方式,以及他们的优缺点,如何准确的判断数组类型
类型 | 优点 | 缺点 |
---|---|---|
typeof |
能准确判断一个变量是否为原始类型 | 判断引用类型时略显乏力,除了函数判断为function外,其他都判断为object |
instanceof |
能判断引用类型的具体是什么类型对象 | |
toString |
利用Object 对象的继承性,toString() 返回[object type] ,其中type 是对象的类型 |
如果此方法在自定义对象中未被覆盖 ,toString 才会达到预想的效果,事实上,大部分引用类型比如Array、Date、RegExp 等都重写了toString 方法。 |
5.类型转换
因为JS是弱类型语言,所以类型转换会经常发生。类型转换分两种:隐式转换即程序自动进行的类型转换,强制转换即我们手动进行的类型转换。
类型转换规则
转换前类型 | 转换前值 | 转换后(Boolean) | 转换后(Number) | 转换后(String) |
---|---|---|---|---|
Boolean |
true |
- | 1 |
true |
Boolean |
false |
- | 0 |
false |
Number |
123 |
true |
- | 123 |
Number |
Infinity |
true |
- | Infinity |
Number |
0 |
false |
- | 0 |
Number |
NaN |
false |
- | NaN |
String |
‘ ’ | false |
0 |
- |
String |
123 |
true |
123 |
- |
String |
123lyichao |
true |
NaN |
- |
String |
lyichao |
true |
NaN |
- |
Symbol |
Symbol() |
true |
TypeError |
TypeError |
Null |
null |
false |
0 |
null |
Undefined |
undefined |
false |
NaN |
undefined |
Function |
function(){} |
true |
NaN |
function(){} |
Object |
{} |
true |
NaN |
[object Object] |
Array |
[] |
true |
0 |
`` |
Array |
["lyichao"] |
true |
NaN |
lyichao |
Array |
["123","lyichao"] |
true |
NaN |
123 ,lyichao
|
if语句和逻辑语句
在if
语句和逻辑语句中,如果只有单个变量,会先讲变量转换成Boolean
,只有下面几种情况会转换成false
,其他都会被转换成true
null,undefined,'',NaN,0,false
各种数学运算符
我们在对各种非Number
类型运用数学运算符(- * /
)时,会先讲非Number
类型转换为Number
类型
1 - true // 0
1 - null // 1
1 * undefined // NaN
2 * ['5'] // 10
⚠️注意!加法是个例外,执行加法运算时:
- 1.当一侧为
String
类型时,会被识别为字符串拼接,并会优先将另一侧转换为字符串类型; - 2.当一侧为
Number
类型,另一侧为原始类型,则将原始类型转换为Number
类型; - 3.当一侧为
Number
类型,另一侧为引用类型,则将引用类型和Number
类型转换成字符串后拼接。
123 + '123' // 123123 (规则1)
123 + null // 123 (规则2)
123 + true // 124 (规则2)
123 + {} // 123[object Object] (规则3)
==
使用==
时,若两侧类型相同,则比较结果和===
相同,否则会发生隐式转换。使用==
时会发生转换可以分为几种情况(只考虑两侧类型不同时):
-
NaN
NaN
和其他任何类型比较永远返回false
(包括和他自己)。NaN == NaN // false
-
Boolean
Boolean
和其他任何类型比较,Boolean
首先被转换为Number
类型。true == 1 // true true == '2' // false true == ['1'] // true true == ['2'] // false
这里注意一个可能会弄混的点:
undefined、null
和Boolean
比较,虽然undefined、null
和false
都很容易被想象成假值,但是他们比较结果是false
,原因是false
首先被转换成0
:undefined == false // false null == false // false
-
String和Number
String
和Number
比较,先将String
转换为Number
类型。123 == '123' // true '' == 0 // true
-
null和undefined
null == undefined
比较结果是true
,除此之外,null、undefined
和其他任何结果的比较值都为false
。null == undefined // true null == '' // false null == 0 // false null == false // false undefined == '' // false undefined == 0 // false undefined == false // false
-
原始类型和引用类型
当原始类型和引用类型做比较时,对象类型会依照
ToPrimitive
规则转换为原始类型:'[object Object]' == {} // true '1,2,3' == [1, 2, 3] // true
来看看下面这个比较:
[] == ![] // true
!
的优先级高于==
,![]
首先会被转换为false
,然后根据上面第三点,false
转换成Number
类型0
,左侧[]
转换为0
,两侧比较相等。[null] == false // true [undefined] == false // true
根据数组的
ToPrimitive
规则,数组元素为null
或undefined
时,该元素被当做空字符串处理,所以[null]、[undefined]
都会被转换为0
。所以,说了这么多,推荐使用
===
来判断两个值是否相等...
为什么0.1+0.2!==0.3
首先先来看下简单的函数计算
function judgeFloat(n, m) {
const binaryN = n.toString(2);
const binaryM = m.toString(2);
console.log(`${n}的二进制是 ${binaryN}`);
console.log(`${m}的二进制是 ${binaryM}`);
const MN = m + n;
const accuracyMN = (m * 100 + n * 100) / 100;
const binaryMN = MN.toString(2);
const accuracyBinaryMN = accuracyMN.toString(2);
console.log(`${n}+${m}的二进制是${binaryMN}`);
console.log(`${accuracyMN}的二进制是 ${accuracyBinaryMN}`);
console.log(`${n}+${m}的二进制再转成十进制是${to10(binaryMN)}`);
console.log(`${accuracyMN}的二进制是再转成十进制是${to10(accuracyBinaryMN)}`);
console.log(`${n}+${m}在js中计算是${(to10(binaryMN) === to10(accuracyBinaryMN)) ? '' : '不'}准确的`);
}
function to10(n) {
const pre = (n.split('.')[0] - 0).toString(2);
const arr = n.split('.')[1].split('');
let i = 0;
let result = 0;
while (i < arr.length) {
result += arr[i] * Math.pow(2, -(i + 1));
i++;
}
return result;
}
judgeFloat(0.1, 0.2);
judgeFloat(0.6, 0.7);
为什么会出现这种情况呢?原因就是出现了精度丢失。
计算机中所有的数据都是以二进制
存储的,所以在计算时计算机要把数据先转换成二进制
进行计算,然后在把计算结果转换成十进制
。
由上面的代码不难看出,在计算0.1+0.2
时,二进制
计算发生了精度丢失,导致再转换成十进制
后和预计的结果不符。