面试官:[]==![]为什么返回true?
转换规则
我总结了以下==
的隐式转换规则,一些简单的就忽略掉了:
类型相同的比较:
(1)如果类型是 Undefined 或 Null,返回true
。
null == null; // true
如果一个是 +0,另一个是 -0,返回true
:
+0 == -0; // true
(3)如果类型是对象,二者引用同一个对象,返回true
,反之返回false
。
{} == {}; // false
null 和 undefined 比较: 仅当它们之间的比较时,返回 true。
null == undefined; // true
NaN 比较: NaN 与任何值比较都返回 false,包括NaN自己。
NaN == NaN; // false
字符串与数字比较: 如果其中一个操作数是字符串,另一个是数字,将字符串转换为数字,然后进行比较。
'5' == 5; // true,将字符串 '5' 转换为数字 5 进行比较
布尔值与非布尔值比较: 布尔值无法直接比较,需先将布尔值转换为数字(true 转换为 1,false 转换为 0),非布尔值也需要转换成数字再比较。
true == 1; // true,将布尔值 true 转换为数字 1 进行比较
false == '123'; // false,将布尔值 false 转换为数字 0 ,将字符串 '123' 转换为数字 123 进行比较
对象与原始类型比较:
如果其中一个是对象,另一个是原始类型,将对象通过ToPrimitive
转换为原始类型,然后进行比较。(即如果原始类型为字符串,则对象转换成字符串再比较;如果原始类为布尔值,则将布尔值与对象都转换成数字进行比较;如果原始类为数字,则将对象转换成数字进行比较。)
{} == 1; //false
ToPrimitive
在这篇文章中详细介绍过,这里我们直接当做公式套用。
ToPrimitive(obj, Number) ==> Number({})
- 如果 obj 是基本类型,直接返回
- 否则,调用 valueOf 方法,如果得到原始值,则返回
- 否则,调用 toString 方法,如果得到原始值,则返回
- 否则,报错
首先{}
先被ToPrimitive
转换成字符串"[object Object]"
,就相当于直接判断"[object Object]" == 1
,字符串与数字的比较中,又要将字符串转换成数字,"[object Object]"转换成数字为NaN
,而NaN
与任何值比较都为false
。
所以{} == 1
返回false
。
关系转换图如下:
回到[] == ! []
这里判断[]
与![]
,两边都是对象,那怎么比呢,我们发现右边还有一个!
,我们知道!
的优先级是要大于==
的,那么先进行非运算。
关于!
!
,文档这样描述:Annotated ES5 11.4.9!
这个运算符会进行两步操作:对
!
后面的操作数转换成布尔值将这个布尔值取反
![]
,将[]
转换成布尔值,我们在官方文档中就知道了,任何对象转换成布尔值都得到ture
,然后在取反,得到![]
为false
。原式[] == ![]
经过!
运算将等式右边转换成了false
。即[] == false
。
接着,根据==
隐式转换规则,等式两边为对象和布尔,那么它们都应该转换成数字进行比较。
[]
经过ToPrimitive
会被转换成字符串""
。再将等号两边的字符串""
和布尔值false
转换成数字0
,得到0 == 0
。打印得到true
。
所以实际在进行[] == ! []
判断时,在JS引擎内部,会将这行代码执行成这个样子:
[] == ![]
[] == !true // 将空数组这个对象类型转换成布尔值
[] == false // ! 运算符对 true 进行取反
'' == false // 对 [] 进行 ToPrimitive 操作,返回一个空对象
0 == 0 // 将等号两边都转换成数字类型