JS中的==(抽象相等)和ToPrimitive()
在ECMA-262 6th的第7.2.12节中,详细说明了==
的操作步骤。
如下图
这张图我来用中文翻译一下
左边是x,右边是y
1.如果x的
在ECMA-262 6th的第7.2.12节中,详细说明了==
的操作步骤。
如下图
这张图我来用中文翻译一下
左边是x,右边是y
1.如果x的类型等于y的类型,那么返回x===y
2.如果x是null,y是undefined,那么返回true
3.如果x是undefined,y是null,那么返回true
4.如果x是number,y是string,那么把y转成数字再和x比较x==ToNumber(y)
5.如果x是string,y是number,那么x转成数字再和y比较ToNumber(x)==y
6.如果x是boolean,那么把x转成数字再和y比较ToNumber(x)==y
boolean转换成数字的时候true是,false是0
7.如果y是boolean,那么把y转成数字再和x比较x==ToNumber(y)
8.如果x是string,number或者symbol,y是object,那么执行x==ToPrimitive(y)
9.如果x是object,y是string,number或者symbol,那么执行ToPrimitive(x)==y
==
的规则就这么简单,那这个ToPrimitive是什么呢?说白了就是把取对象的原值,原值是一个基本类型。
那怎么取呢?其实就是通过valueOf和toString来取原值。那什么时候用valueOf,什么时候用toString呢?
规则是先调用valueOf,如果valueOf()返回值不是基本类型,那么调用toString(),如果valueOf和toString均不返回基本类型值,那么抛出错误。
下面这个表是JS内置类型,除去对象,剩下的就是JS基本类型
1 | null 空值 |
1 | var o = { |
这段代码能够看出来,对这个o进行ToPrimitive()操作时最终会调用toString,因为o.valueOf()返回的不是基本类型值,所以调用toString()。
同理array、function等的valueOf均不返回基本类型值,所以他们的ToPrimitive()都会调用toString()。
那什么时候会调用valueOf呢?
1 | var a=new Number(10); |
如上面代码所示,这个a是一个包装对象,他的valueOf是10,一个基本类型,所以此时ToPrimitive()会调用valueOf。
懂了ToPrimitive(),那么上面的8和9就应该没问题了。
然后看几个常见的令人困惑的==
1 | console.log(false == "");//true |
根据==
的规则,此时x是boolean,所以把false转成数字0,然后变成0 == “”,再根据规则x是nubmer,y是string,所以把””转成数字0。
顺便说明一下””和[]都会被转成0
1 | Number("");//0 |
再看一个
1 | console.log([] == ![]);//true |
让人不敢相信的是居然是true,但是根据规则,确实应该是true。
首先右边!会导致强制布尔转换,ToBoolean会把[]转成true(因为Boolean(Object)会是true),然后再加上!,所以变成false。此时等同于[] == false
,根据规则,y是boolean,所以对y进行ToNumber()变成[] == 0
,然后此时x是object,y是number,所以对x进行ToPrimitive(),由于x是空数组,所以执行toString之后会是””,此时变成"" == 0
。然后x左边是string,右边是number,所以对x进行ToNumber变为0,所以最终经过重重转换变为0 == 0
,所以结果是true。
经过上面的分析,我想应该已经对==
不会再陌生了。