之前我写过一篇关于==操作符的文章,当时觉得还行,看完《你不知道的javascript 中卷》之后才觉得汗颜。之前的文章错漏百出,也毫无章法,因此参考该书及ECMAScript相关规范,重新整理了一下==操作符的相关规则。
关于对象的抽象化操作(toprimitive)的介绍,可以参考我上一篇文章戳我
在进行==比较时,也会发生隐式类型转换(对象会执行toprimitive操作),这里就介绍一下进行==比较的时候的规则。
一、==的含义
==又叫宽松相等(或称不全等),===又叫严格相等(或称全等)。
以往,关于==和===的区别,我见到的最多的说法是,==比较值,===比较值及类型。
但是,在书中,作者提出,==允许在比较的时候进行类型转换,===不允许在比较的时候进行类型转换。
以上这两种说法,各位自行斟酌。
二、==比较时的规则
ECMAScript 11.9.3(中文版)是这么说的:
- 若Type(x)与Type(y)相同, 则
- 若Type(x)为Undefined, 返回true。
- 若Type(x)为Null, 返回true。
- 若Type(x)为Number, 则
- 若x为NaN, 返回false。
- 若y为NaN, 返回false。
- 若x与y为相等数值, 返回true。
- 若x 为 +0 且 y为−0, 返回true。
- 若x 为 −0 且 y为+0, 返回true。
- 返回false。
- 若Type(x)为String, 则当x和y为完全相同的字符序列(长度相等且相同字符在相同位置)时返回true。 否则, 返回false。
- 若Type(x)为Boolean, 当x和y为同为true或者同为false时返回true。 否则, 返回false。
- 当x和y为引用同一对象时返回true。否则,返回false。
- 若x为null且y为undefined, 返回true。
- 若x为undefined且y为null, 返回true。
- 若Type(x) 为 Number 且 Type(y)为String, 返回comparison x == ToNumber(y)的结果。
- 若Type(x) 为 String 且 Type(y)为Number,
- 返回比较ToNumber(x) == y的结果。
- 若Type(x)为Boolean, 返回比较ToNumber(x) == y的结果。
- 若Type(y)为Boolean, 返回比较x == ToNumber(y)的结果。
- 若Type(x)为String或Number,且Type(y)为Object,返回比较x == ToPrimitive(y)的结果。
- 若Type(x)为Object且Type(y)为String或Number, 返回比较ToPrimitive(x) == y的结果。
- 返回false。
下面来个总结。
1、简单值的比较
如果两个值同类型,比较两个值。其中需要特别说明的是,NaN!=NaN。对象的话比较指针。
如果两个值类型不相同,那么:
- null==undefined;
- null、undefined与其他值的==比较结果均为false;
- 如果是字符串与数字比较,则将字符串强制转换成数字(调用Number()),然后返回两个数字比较的结果;
-
如果是布尔值与其他值比较,则将布尔值转换成数字,然后返回数字与其他值的比较的结果;
因为参考的是ecma5的规范,没有包括Symbol值。按照我的经验及搜索到的资料,Symbol值与其他类型的值比较时均为false,与同类型(Symbol类型)的值比较时,除for()方法传入相同参数得到的值,即使描述符相同也为false。(这一条如有错误欢迎指正)
let a=Symbol('aaa');
let b=Symbol.for('aaa');
let c=Symbol.for('aaa');
console.log(a==b)
console.log(a==c)
console.log(b==b)
2、对象与简单值的比较
对象与简单值的比较,分以下几种情况:
- 如果简单值是nul或者undefined,结果是false;
- 如果简单值是字符串或者数字,对对象进行toprimitive操作进行类型转换,类型是number,然后返回比较的结果;
- 如果简单类型是boolean,先将boolean值强制转换成数字,然后遵循第二条规则,返回比较的结果;
- 如果简单类型是Symbol值,会对对象进行toprimitive操作,类型是number,然后返回比较的结果;
let a = Symbol.for("aaa");
let b = Symbol.for("aaa");
let obj = {
valueOf() {
return "aaa";
},
toString() {
return "a";
}
};
let obj1 = {
valueOf() {
return a;
},
toString() {
return "a";
}
};
let obj2 = {
valueOf() {
return "0";
},
toString() {
return "a";
}
};
console.log(obj == "aaa", "对象执行toprimitive操作,类型是number"); //
console.log(obj1 == b, "对象执行toprimitive操作,类型是number"); //
console.log(obj2 == false,"boolean先转换为数字,对象执行toprimitive操作,类型是number");
注意的一点是,包装类型的也是对象,默认情况下,包装类型的对象进行toprimitive的的结果是对应的简单类型的值。但是,如果自己更改了原生对象的原型上的valueOf()方法(Date对象对应的是toString()方法),结果会不同。
let a=new Boolean(2);
let b=new Boolean(2);
a.valueOf=function(){
return 2
}
// Boolean.prototype.valueOf=function(){
// return 2
// }
console.log(a==2)
console.log(b==2)
3、Date对象
上一篇文章说过,Date对象有别于其他对象。其他对象进行隐式类型转换时默认类型是number,Date对象默认则是string。那么比较的时候呢?测试一下就知道了。
let currentDate = new Date();
let a = currentDate.valueOf();
let b = currentDate.toString();
console.log(a == currentDate,"//如果为true则Date对象toprimitive默认类型是number");
console.log(b == currentDate,"//如果为true则Date对象toprimitive默认类型是string");
所以,Date对象在进行==的时候,进行toprimitive操作的类型是string。
4、特别提醒
需要特别注意的是,==的结果并不能传递。即:a==b,b==c并不能得出a==c。因为进行==比较时,转换的规则会根据值的类型有所不同。
console.log(0 == "0");
console.log(0 == "");
console.log("0" == "");
三、结语
以上就是==比较的规则,ECMAScropt6的规范原文我没找到关于==的解释,所以用的还是ECMAScript5的规则。如有错漏还望指正。
参考资料;ECMAScript规范中文版11.9.3 戳我
《你不知道的javascript 中卷》
我的上一篇文章:戳我