隐式类型转换和 ==、=== 的常见陷阱怎么解释?

先理解原理和示例,再练习完整口述与追问。

先判断类型;对象参与转换先 ToPrimitive;+ 可能连接字符串;== 按类型组合转换;NaN、正负零分别理解。

严格相等 === 一般不会为了比较去转换类型,不同类型直接返回 false;同类型数值中 NaN 不等于自身,+0 与 -0 相等。Object.is 把 NaN 看作相同,并区分 +0 与 -0,因此它和 === 的差别集中在这两处。

宽松相等 == 并不是“两边全部转成数字”。null 与 undefined 宽松相等,但 null == 0 为 false;布尔值参与比较会先转成数值,所以 false == 0 为 true;字符串与数值比较通常将字符串转成数值,因此 '' == 0 为 true。对象与原始值比较时,对象先经过 ToPrimitive,可能调用 Symbol.toPrimitive,或按提示调用 valueOf、toString。[] == false 为 true,可理解为 false 变成 0,数组转成空字符串,再转成 0;两个不同数组 [] == [] 仍为 false,因为比较对象身份。

加号先把操作数转为原始值;只要其中一个是字符串,就做字符串拼接,所以 1 + '2' 得到 '12'。减号等算术运算走数值转换,'2' - 1 得到 1;Number 与 BigInt 也不能随意混合做算术。业务代码优先用 ===,需要兼容 null/undefined 时可以明确写 value == null,并解释它只针对这两种空值。

参考:MDN Equality。

逐步推演 [] == ![]:![] 先得到 false;布尔值转为 0;数组按原始值转换得到空字符串;空字符串在本次数值比较中为 0,结果 true。另一个例子 null == 0 为 false,说明宽松比较不是无条件两边都转数字。

不要用这类技巧压缩业务判断。对象自定义转换可能有副作用,比较不可信对象前应先定义可接受数据形状。

严格相等 === 一般不会为了比较去转换类型,不同类型直接返回 false;同类型数值中 NaN 不等于自身,+0 与 -0 相等。Object.is 把 NaN 看作相同,并区分 +0 与 -0,因此它和 === 的差别集中在这两处。