JavaScript 中的宽松相等(==)及强制类型转换

Sep 18, 2016 JavaScript https://git.io/vAUox

同步知乎回答:https://www.zhihu.com/question/46943112/answer/122096589

1
[] == ![]

结果为 true,也就是说这个等式是成立的

已经有人回答这个了,但是还没给出解释,这里我就来详细写一下:

首先解释一下为什么这段代码令人惊叹:我们都知道 JavaScript 中唯一一个非自反(non-reflexive)的值是 NaN,而在这里乍看之下,普通的字面量空数组居然也是“非自反”,岂不矛盾?
另外这个“等式”在非等宽字体里看起来挺像一个颜文字 []==![]

这个问题在某些人看来应该算是 JavaScript 的 Bad Part,但是搞懂这个问题对 JS 的强制类型转换的理解还是有帮助的,也可以避免在自己的代码中出现类似的问题

解释这个“等式”至少要四句话,涉及到了 JavaScript 的运算符优先级 、宽松相等(即 ==)的判断过程以及强制类型转换

  1. 等号右边有 ! ,优先级比 == 更高,优先计算右边的结果。 [] 为非假值(参考,什么是假值:Falsy - Glossary),所以右边的运算结果为 false,即:

    1
    ![] ==> false  // 此处表示转换过程,下同
  2. == 的任意一边有 boolean 类型的值时先把这个值转换成 number 类型,右边转换成了 0 ,即:

    1
    Number(false) ==> 0
  3. == 的两边分别是 number 和 object 类型的值时,把 object 转换成 number 类型,需要对 object 进行 ToNumber 操作,即:

    1
    Number([].valueOf()) ==> 0
  4. 至此,== 两边的值都变成 0 了,显然是成立的

问题来源于 You don’t know JS 一书的 Types & Grammar 部分:You-Dont-Know-JS/ch4.md at master · getify/You-Dont-Know-JS · GitHub(中文《你不知道的Javascript(中卷)》)

书中列出了宽松相等详细的判断和强转规则,但是对于这个问题没有作具体回答,所以我就来写一下

=== Update 2016-09-17 ===

关于原等式是否可以等价于(评论区 @宋一喵 提出):

1
new Array() != new Array()

这里是不可以的

首先, ==(宽松相等,及其相对的 != )和 ===(严格相等,及其相对的 !== )在判断两个 object 类型的值的时候,工作原理是一样的,都是判断两个对象是否指向同一个值(换句话说就是地址相同)

在 new Array() != new Array() 里,或者 new Array() !== new Array() ,比较的是两个对象,这中间是没有强制类型转换的。然而原等式中右边有优先级更高的 ! ,所以原等式相当于 new Array() != false,就必须要有强制类型转换才能比较了

另外宽松相等还有一个坑,就是大部分 object 对象包括空字面量对象 {} 在跟强制类型转换过程中会出现的 number 类型的值比较时,object 的值会转换成 NaN,跟任何值比较都是不相等的。而在跟字符串比较的时候又会转化成 “[object Object]”

1
2
3
4
5
Number({}) ==> NaN        // 这里表示转换的过程,这个等式并不成立
Number.isNaN(Number({})) // true

{} == "[object Object]" // true
{} == 0 // false 看起来好像显然,但实际是 NaN != 0

=== Update 2016-09-18 ===

关于这个问题是不是 It’s not a bug. It’s a feature (由评论区 @何志宇 提出)的回复

ECMAScript 规范里规定了宽松相等的判定规则(参考: ECMAScript Language Specification)。既然然此等式是严格按照规范进行的转换和判断,那就不能说是 JavaScript 引擎的 bug

然后有人又要问了,既然这个问题这么蛋疼,实际编程中也几乎不会用到,为什么不直接屏蔽这些特殊情况呢。请阅读下面的扩展阅读后自行思考总结

发散思考:浮点数的精度问题算是 bug 还是 feature 呢?

1
0.1 + 0.2 != 0.3

扩展阅读:为什么 Windows 10 计算器应用中含开方计算结果不准确? - 数学