这两个表达式涉及到 JS 中类型转换和比较操作符的行为。JS 是一种灵活的动态语言,它有一套自己的类型转换规则,有时会导致一些看似奇怪的结果。在理解这两个表达式的行为时,需要深入了解 JS 的类型转换机制以及比较操作符的工作方式。
首先介绍一下 JS 中的类型转换机制,JS 中的类型转换通常发生在两种情况下:隐式类型转换 和 显式类型转换。隐式类型转换是 JS 自动执行的类型转换,通常发生在需要不同类型的值的运算或比较时。而显式类型转换是通过一些内置函数(如parseInt()
、parseFloat()
、String()
、Number()
等)来显式地进行类型转换。
== 的隐式类型转换
特例:
- null == undefined 为 true;
- NaN不与任何值相等,包括它自己;
- null和undefined不会进行数据转换;
1.数值和非数值类型比较
- 数值和字符串
1console.log('Ywis' == 1)
2console.log('' == 0)
3console.log('1' == 1)
4console.log('000' == 0)
当一个数据类型是数值,另一个是字符串,会将字符串转换为数值,再与之比较。比如其中'Ywis'
会转换为数字NaN
,所以返回false
。
- 数值和布尔值
1console.log(true == 1);
2console.log(true == 2);
3console.log(false == 0);
4console.log(false == 1);
很容易就能看出,这样的比较,会将true
转化成数值1
,false
转化为0
。
- 数值和数组
1console.log([] == 1);
2console.log([] == 0);
3console.log(['Ywis'] == 1);
4console.log([1,2] == 2);
在这里,数组会先通过调用toString()
转换为字符串后再转化为数值。比如['Ywis']
先转化为字符串'Ywis'
后再转化为数值NaN
,所以返回false
。
拓展:数组、对象和函数在与其他基本数据类型进行比较时都会先转换为字符串,然后再转换为相应的数据类型。
2. 布尔值和非布尔值类型比较
1console.log(true == 1);
2console.log(true == '00001');
3console.log(false == []);
4console.log(true == ['Ywis']);
当布尔值和非布尔类型比较,会将true
转换为 1,false
转换为 0。将非布尔值类型统统转化成数值,这里的类型转换规则和上面提到的相同。
3. 对象和原始值的比较
1console.log({} == 0);
2console.log([] == 1);
当对象与原始值进行比较时,JS 会先尝试通过调用对象的valueOf()
方法来获取原始值。如果valueOf()
方法返回的是一个原始值,JS 会将其转换成与待比较的原始值相同的类型,然后进行比较。如果valueOf()
方法返回的还是一个对象,则会继续调用对象的toString()
方法,将其返回值转换为原始值,然后再进行比较。
比如{} == 0
,对于空对象{}
它的valueOf()
方法返回的是对象本身,因此 JS 会继续调用 toString()
方法,返回得到一个字符串 "[object Object]"
,再转换为数值NaN
,所以返回false
。
逻辑非 ---!
在操作非布尔值类型时,会进行两步操作:
- 将该数据类型先转换成布尔值类型。
- 再进行取反操作。
1console.log(!"");
2console.log(![]);
3console.log(!{});
4console.log(!(-4));
注意:
false
:布尔值 falseundefined
:未定义的值null
:空值0
:数字 0-0
:负零NaN
:非数字值''
:空字符串
除了以上七个值外,其他值(包括对象、数组、函数等)在转换为布尔值时都会返回 true
。这种规则使得 JS 中的大多数值被视为真值(truthy)。
这是很好理解的,在 JS 中对象(包括数组)的比较是基于它们的引用地址,而不是它们的内容。即使两个对象的内容相同,它们的引用地址也不同,所以比较结果是 false
。
过程:
- 因为
!
的优先级高于==
,首先将空数组转换为true
,再取反得false
; - 接着是
[] == false
,这是布尔值和非布尔值类型比较,首先将false
转换为0
,然后调用对象的toString
方法,返回一个空对象''
- 最后空对象转换为数值
0
,即0 == 0
,结果返回true。