在js中使用==进行的比较叫做相等比较,判断语句中经常会用到==进行相等比较。如果两个值不是同一个类型,js引擎就会在背后“偷偷”做一些事情(隐式类型转换)进行处理。
简单示例
以下代码示例,可能第一感觉第一个返回true,第二个返回true,其实不然
[] == 0 // true
[0] == [0] // false
两个操作在进行相等比较时,如果两个操作数类型不一致的时候,就会根据一定的规则进行强制类型转换,转换完之后再进行比较。
转换规则
在两个操作数类型不一致时,比较转换规则如下:
-
两个操作数中如果有布尔值,则先将布尔值转换成数值--false会转换成0,true会转换成1
-
两个操作数分别为字符串和数值,则先将字符串转成数值
-
一个操作数为对象,另外一个操作数不是对象,则调用valueOf方法获取原始值,然后再进行比较;如果调用valueOf方法不能获取原始值,则使用toString方法获取原始值,然后进行比较
-
null和undefined是相等的
-
null和undefined不能转化成其他值进行比较
-
操作数其一是NaN,相等操作会返回false,即使另外一个操作数也是NaN
-
两个操作数都是对象的时候,会比较它们是不是同一对象,同一对象返回true,不同对象返回false
根据上述的转换规则不难分析以上代码是如何比较的:
-
[] == 0
-
调用[]数组的vlaueOf方法,但返回值是数组本身,不是基本类型的值
-
调用[]数组的toString方法,此时会返回空字符串('')
-
因由一操作数为数字,空字符串进行强制类型转换为数值:Number('') 返回0
-
[] == 0最终会转换成0 == 0,所以返回true
-
-
[0] == [0]
-
两个操作数非同一个数组对象,根据比较规则返回false
-
特殊相等比较
根据比较规则,一些比较容易出错的相等比较如下:
true == 1 // true
false == 0 // true
'5' == 5 // true
NaN == 1 // false
NaN == NaN // false
null == undefined // true
null == 0 // false,不发生强制类型转换
undefined == 'undefined' // false,不发生强制类型转换
[] == 0 // true
[0] == [0] // false
“奇葩”场景
由于对象与基本类型值进行相等比较时,会先调用对象的valueOf、toString方法,只要我们改写了其中一个方法使其返回基本类型值,就可以写出来一些很“奇葩”的代码。
var value = {
index: 0,
toString() {
return this.index++
}
};
value == 0 && value == 1
在上述代码中,重写了对象的toString方法,根据对象与基本类型值的比较规则,每次比较会调用toString方法返回基本类型值,之后index会增加1,所以上述代码value == 0 && value == 1会返回true。
替代方案
由于==比较会在我们看不到的地方“偷偷”做一些事情,这就导致了代码的运行结果可能有时候并不符合我们的预期,增加了我们排查问题的难度,建议在代码使用===比较(全等比较),全等比较不会做隐式类型转化。
===比较也存在一个小小的问题,NaN在全等比较的时候也是不等于自身的,并且也无法比较+0与-0。当需要比较的值有NaN、+0、-0等的时候,可以使用ES6+新增的方法Object.is进行比较。
Object.is(NaN, NaN) // true
Object.is(+0, -0) // false
Object.is(+0, +0) // true
Object.is(-0, -0) // true
本文首发于微信公众号(Web前端技术),关注公众号及时获取最新内容~