因为不太搞得懂js的隐式类型转换,刚好看到了,所以学习了一下
==是如何进行比较的
首先放个官方链接
如果类型相同
null == null // true
undefined == undefiend // true
+0 == -0 // true 因为在IEEE754协议中,0的补码相同
{} == {} // false 大部分情况下,对象之间不能比较,因为对象是引用
let a={};let b=a;a==b // true 此时引用相同对象返回true
NaN == NaN // false NaN与任何值比较都是false,具体可先下方
如果类型不同
如果两边类型不相同,会进行一次隐式转换,基本类型的隐式转换相对简单,这里就不在赘述(即false转0,'123’转123之类的)
而对象与原始类型的转换,则是重点也是难点
对象的隐式转换,实际上是调用了ToPrimitive
这个函数
ToPrimitive
在传入基本数据类型的时候不会进行转换,而传入的是对象的时候,会根据需要的类型来转换成对应的原始值(原始值是固定而简单的值,是存放在栈中的简单数据段,也就是说,它们的值直接存储在变量访问的位置,可以简单理解为原始类型)
Obj2Number
ToPrimitive(obj,Number)
如果obj不是基本类型,会先调用valueOf()
方法,如果 valueOf
返回的是一个原始值,那么就返回该原始值,否则会调用 toString
方法,如果 toString
方法返回原始值,则返回原始值,否则会报错,下面我们举个例子
a = {}
console.log(Number(a)) // NaN
a
是对象,并非基本类型- 在没有重写
valueOf
的情况下,valueOf
返回对象本身,不是原始值 - 调用toString方法,
{}
的toString
方法返回"[object Object]"
字符串
在进行了这样的转换之后,原始代码可以写成为 console.log(Number("[object Object]"))
这样,就变成了字符串转数字,打印结果为NaN
Obj2String
其和对象转数字的类型类似,只不过顺序不同,调用toString
和调用valueOf
顺序变化,基本上就这个区别
Obj2Boolean
对象转Boolean
则永远为true
二元运算符+
将 +
左右两边的数据分别用ToPrimitive
函数调用一遍,如果返回值中有一个是字符串,则进行字符串拼接;否则进行加法运算
console.log(1+'2') // '12'
console.log([]+[]) // []在调用ToPrimitive时,调用toString变为'',原等式可变为
-> console.log(''+'') // ''
console.log([]+{}) // {}在调用ToPrimitive时,调用toString变为'[object Object]',原等式变为
-> console.log(''+'[object Object]') // '[object Object]'
console.log({}+{}) // '[object Object][object Object]'
一元运算符 !
官网其实解释了运算符怎么运行的,但我们这里还是简单点说
- 对
!
后面的操作数转换为Boolean
- 将这个布尔值取反
重新分析[]==![]
我们把这个等式加点()
看看
[] == (![])
//先运算!运算符,[]转为布尔值,又因为对象的布尔值为true
[] == (!true)
//因此之后变为
[] == false
//然后我们对右边的数组执行ToPrimitive,即[].toString()=''
'' == false
//左边String右边Boolean,会转换到数字进行比较
0 == 0 //两边都转为数字
拓展:隐式转换分析
之前有一张很火的图
我们选两个讲
[] + {} // "[objec Object]" 这个应该比较好理解
{} + [] // 0
//这是因为,在某些(大部分)浏览器中,他们把{}作为区块语句而非对象,这也是为什么在Chrome中
{} + {} // NaN
({}) + {} // '[objec Object][objec Object]'
//在这种情况下,其实+号作为1元运算符,即
{} (+[])
//这样就是{}0,即返回0
(!+[]+[]+![]).length //9
//其实第一眼看到这个9,确实很难理解,但我们如果给每一个符号加上括号表示优先级
(!(+[])+[]+![])
//然后我们一步一步的调用ToPrimitive
(!(+0)+[]+![]) //+作为一元运算符,后面的数转为Number
(true+[]+![]) //!0转换为布尔,即为true
(true+''+![]]) // []调用ToPrimitive,返回''
('true'+![]) //true转换为字符串
('true'+!true)// !后转为boolean,对象默认为true
('true'+false) // 转为false
('truefalse').length //返回9
参考文献
[面试官 :] == ! [] 为什么返回 true ? - 掘金 (juejin.cn)
Q.E.D.