因为不太搞得懂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
  1. a是对象,并非基本类型
  2. 在没有重写 valueOf的情况下,valueOf返回对象本身,不是原始值
  3. 调用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]'

一元运算符 !

官网其实解释了运算符怎么运行的,但我们这里还是简单点说

  1. !后面的操作数转换为Boolean
  2. 将这个布尔值取反

重新分析[]==![]

我们把这个等式加点()看看

[] == (![])
//先运算!运算符,[]转为布尔值,又因为对象的布尔值为true
[] == (!true)
//因此之后变为
[] == false
//然后我们对右边的数组执行ToPrimitive,即[].toString()=''
'' == false
//左边String右边Boolean,会转换到数字进行比较
0 == 0 //两边都转为数字

拓展:隐式转换分析

之前有一张很火的图

72cb0fce68ee22bcd599e5b7c0b5474

我们选两个讲

[] + {} // "[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.