宽松相等(==)和严格相等(===)
为什么这个问题的讨论要放在类型转换这里呢?
首先,这里有一个常见的误区,==是检查值是否相等,===是检查值和类型是否相等
然而JavaScript中实际上是
==允许在比较中进行强制类型转换,而===不允许
首先在性能上,两者差别不大(大概百万分之一秒)
我们考虑的只有需不需要强制类型转换这一点而已
抽象相等(==)
ES5中"抽象相等比较法"定义了==运算符的行为
其中规定如果两个值类型相同,就仅仅比较它们是否相等
如果两个值类型不同,==会发生隐式强制类型转换,会将其中之一或者两者都转换为相同的类型后再比较
但是有几个特殊情况需要注意
- NaN不等于NaN
- +0等于-0
然后还定义了对象的宽松相等==,两个对象指向同一个值的时候即视为相等(比较对象的时候==和===是完全一样的,都不会发生强制类型转换)
常见情况
- 字符串和数字之间的比较
- 其他类型和布尔类型之间的比较
- null和undefined之间的比较
- 对象和非对象之间的相等比较
1. 字符串和数字之间的比较
如果==运算符中有一边是字符串,另一边是数字
则将字符串强制转换为数字后进行比较
如 "42"==42 实际上是 42==42
2. 其他类型和布尔值之间的相等比较
如果==运算符中有一边是布尔值
则将布尔值强制转换为数字后进行比较
"42" == true //false
3. null和undefined之间的比较
如果==运算符中有一边是null,另一边是undefined
则结果为true
也就是说==中null和undefined相等(他们自身也相等)
除此之外其他值都不存在这种情况
4. 对象和非对象之间的相等比较
如果==运算符中有一方是字符串或数字(布尔值会首先被转换为数字)
另一方是对象(对象/函数/数组)
那么对象首先会进行一个ToPrimitive抽象操作转换为对应的基本类型值
ToPrimitive抽象操作通过内部操作[[DefaultValue]]检查是否有valueOf()方法,如果有并返回基本类型值,就使用该值进行强制类型转换,如果没有就使用toString()的返回值(如果存在)来进行强制类型转换,普通对象的toString()会返回内部[[class]]的值,比如"[object Object]",而数组会将所有单元字符串化后用","连接起来,基本类型的封装对象则会由valueOf返回基本类型值
一些例外的情况
Object(null) == null, //false
Object(undefined) == undefined, //false
Object(NaN) == NaN, //false
null和undefined没有封装对象,因此Object(null)返回的是Object()本身
Object(NaN)等同于new Number(NaN),但是拆封后 NaN == NaN 返回一个false值
抽象关系比较
前面说了==时候的比较规则
这里说下a<b中涉及的隐式强制类型转换
(该算法仅针对于a<b,a>b会被处理成b<a)
首先,比较双方先调用ToPrivate,如果结果出现非字符串,就再调用ToNumber将双方强制类型转换为数字来比较
如果双方都是字符串,则按照字母顺序来比较
但是有一点格外需要注意
JavaScript中的a<=b实际上等同于 !(a>b) 然后会被处理成!(b<a)
var a = {
num: 1
}
var b = {
num: 2
}
a > b //false
a == b //false
a < b //false
a <= b //true
a >= b //true
这个例子中a,b都被转为"[object Object]",因此 a<b a>b都返回false,而a<=b和a>=b都返回的是!false也就是true
但是==的时候由于两边都是对象并且并不指向同一个对象,因此返回false