背景:js中的相等比较运算规则十分繁杂。本文以流程的角度去解释非严格全等的行为,尝试帮助我们快速理清js中比较运算的规律。同时还有+0和-0的比较、NaN和NaN的比较。为啥
0 == null
为false。
js中的相等比较方式
首先,了解一下JavaScript中存在的等值比较操作有哪些【1】:
- 抽象相等比较 (“loose equality”,“double equals”) ,使用 x == y。
噩梦开始的地方,可以判断为true的边界很大,不同类型的相等比较,也可以在相等的范围里。
- 严格相等比较 (也被称作全等比较,“strict equality”, “identity”, “triple equals”),使用 x === y。
增加判断的准确性:收窄了js中可以判断为true的边界,简化判断流程。把不同类型却能判为相等的情况,划到不相等的范围。
- 以及
Object.is
(ECMAScript 2015/ ES6 新特性),使用 Object.is(x, y)。增加判断的灵活性:严格相等比较中:
+0 === -0
是相等的,NaN === NaN
是不相等的,Object.is就可以给这两种情况给出相反的结果,其它跟严格相等规则一样。
当js中出现x == y
,es规范中的处理流程
步骤一,先依次判断:
-
1.1 先判断x,然后是y,如果有一个不是正常值(比如抛出一个错误),立即中断执行,结束。否则进入1.2。
-
1.2 如果x和y类型相同,执行严格相等运算
x === y
,并作为结果返回,结束。否则进入步骤二。如果是两个对象,比较的是他们对应的对象所在内存的位置
步骤二,此时x和y类型不同,会根据表格中的搭配,按步骤依次往下匹配(x和y在==的前后位置不要求):
步骤 | x | y | 转换 | 结果 | 下一步 |
---|---|---|---|---|---|
2.1 | null | undefined | 否 | true | 结束 |
2.2 | 数值 | 字符串 | 把字符串转为数值 | ToNumber(字符串) == 数值 | 重新比较结果里的表达式 |
2.3 | 布尔值 | 其它类型 | 把布尔值转为数值 | ToNumber(布尔值) == 其它类型 | 重新比较结果里的表达式 |
2.4 | 对象 | 字符串、数值、Symbol值 | ToPrimitive(对象) | ToPrimitive(对象)== y | 重新比较结果里的表达式 |
2.5 | 任意值 | 任意值 | 否 | false | 结束 |
表格中:
- 如果出现了转换,则要回到步骤一,重新比较结果列里的表达式,直到结束。
ToNumber(参数)
表示把参数转为数值。ToPrimitive(对象)
通过尝试调用 对象 的对象.toString()
和对象.valueOf()
方法,将该对象 转换为原始值(Primitive)。
快速记忆【1,2】:
- 在es规范里,undefind和null只有等于互相时,是true,和其它类型比较都是false。也就是说0 == null结果为false是es规范导致的。更多细节参考mdn
- 转换只会进行(字符串、布尔值)转为数值;(对象)转为原始值两种情况。
上例子
// == 例子
0 == null // false
// 在步骤二中只匹配到2.5,返回false,结束
// 也就是说在es规范里,undefind和null只有等于互相时,是true,和其它类型比较都是false。但特殊情况下,浏览器可能存在null == 对象为ture的情况,具体参阅mdn
false == '' // true
// 在表格里最先匹配到了2.3,会把false转为0,变成0 == ''
// 然后0 == '' 在表格里最先匹配到了2.2,把''转为0,变成0 == 0
// 最后,0 == 0会回到1.2中进行全等比较,并得出最终结果,结束比较
[3,4] == '3,4' // true
// 首先在表格里匹配到了2.4,经过[3,4].toString()转换为'3,4',变成'3,4' == '3,4'
// 最后'3,4' == '3,4'回到1.2进行全等比较
总结来源
【1】 MDN:
非严格相等==
最终的比较方式等同于全等操作符===
的比较方式。相等操作符满足交换律。【2】 读懂es6规范:
- ReturnIfAbrupt(x).
- ReturnIfAbrupt(y).
- If
Type(x)
is the same asType(y)
, thenReturn the result of performing Strict Equality Comparisonx === y
.- If
x
isnull
andy
isundefined
, returntrue
.- If
x
isundefined
andy
isnull
, returntrue
.- If
Type(x)
is Number andType(y)
is String, return the result of the comparisonx == ToNumber(y)
.- If
Type(x)
is String andType(y)
is Number, return the result of the comparisonToNumber(x) == y
.- If
Type(x)
is Boolean, return the result of the comparisonToNumber(x) == y
.- If
Type(y)
is Boolean, return the result of the comparisonx == ToNumber(y)
.- If
Type(x)
is either String, Number, or Symbol andType(y)
is Object, then return the result of the comparisonx == ToPrimitive(y)
.- If
Type(x)
is Object andType(y)
is either String, Number, or Symbol, then return the result of the comparisonToPrimitive(x) == y
.- Return
false
.