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)==yboolean转换成数字的时候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
2
3
4
5
6
7
null 空值
undefined 未定义
boolean 布尔值
number 数字
string 字符串
object 对象
symbol 符号
1
2
3
4
5
var o = {
a: 10
};
console.log(typeof o.valueOf());//object
console.log(typeof o.toString());//string

这段代码能够看出来,对这个o进行ToPrimitive()操作时最终会调用toString,因为o.valueOf()返回的不是基本类型值,所以调用toString()。

同理array、function等的valueOf均不返回基本类型值,所以他们的ToPrimitive()都会调用toString()。
那什么时候会调用valueOf呢?

1
2
var a=new Number(10);
console.log(typeof a.valueOf())//number;

如上面代码所示,这个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
2
Number("");//0
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。

经过上面的分析,我想应该已经对==不会再陌生了。