关于JS的想等运算,由 [] == ![] 引发的思考

前言

之前看到一个思考题,是关于js的相等运算和隐式类型转换的,大部分都还好理解,但是看到了一个看起来很不合常理的结果,让我们一起来探究下。

console.log([] == ![])
// true
复制代码

看以上的题目,左边一个空数组,右侧一个空数组取反,乍一看很容易得出结果是 false,但真正运行的时候才发现结果居然是 true.这是什么原因呢?

隐式类型转换

我去查了下 MDN Web Doc 关于 「相等(==)运算的文档(文档介绍),上面介绍了与严格相等运算符(===)不同,它会尝试强制类型转换并且比较不同类型的操作数。
具体的类型转换规则如下:

  • 如果两个操作数都是对象,则仅当两个操作数都引用同一个对象时才返回true。
  • 如果一个操作数是null,另一个操作数是undefined,则返回true。
  • 如果两个操作数是不同类型的,就会尝试在比较之前将它们转换为相同类型:
    • 当数字与字符串进行比较时,会尝试将字符串转换为数字值。
    • 如果操作数之一是Boolean,则将布尔操作数转换为1或0。
      • 如果是true,则转换为1。
      • 如果是 false,则转换为0。
    • 如果操作数之一是对象,另一个是数字或字符串,会尝试使用对象的valueOf()和toString()方法将对象转换为原始值。
  • 如果操作数具有相同的类型,则将它们进行如下比较:
    • String:true仅当两个操作数具有相同顺序的相同字符时才返回。
    • Number:true仅当两个操作数具有相同的值时才返回。+0并被-0视为相同的值。如果任一操作数为NaN,则返回false。
    • Boolean:true仅当操作数为两个true或两个false时才返回true。

依据上述规则,我们来解析一下 [] == ![] 执行隐式类型转换的过程:

  1. [] 是一个 Array, 也是属于 object 类型(真值),所以![] 会被解析成对对象类型取反得到 false;得到的表达式可以转换成:
    [] == false
    复制代码
  2. 依据不同类型操作数比较规则,操作数之一是Boolean,则将布尔操作数转换为1或0, 则得到新的表达式为:
    [] == 0
    复制代码
  3. 又依据如果操作数之一是对象,另一个是数字或字符串,会尝试使用对象的valueOf()和toString()方法将对象转换为原始值, 尝试将 [] 通过valueOf/toString 方法进行转化;
  4. 因为 [].valueOf() 结果还是 [], 所以继续使用 [].toString() 方法得到表达式:
    "" == 0
    复制代码
  5. 依据当数字与字符串进行比较时,会尝试将字符串转换为数字值 继续转换表达式为:
    0 == 0
    复制代码

最终得到相等的结果。

扩展

({}) == {}
// false, 两个都是object, 但并不指向同一个对象,所以为false
复制代码
({}) == !{}
// false, 和 [] == ![] 不同的是,Object.prototype.toString.call({}) 的结果是 "[object, object]", 并不是空字符串;
// "[object, object]" 被转换成数字的时候是 NaN,所以并不和 0 是相等的
复制代码
NaN == NaN 
// false, 任意一方为 NaN,结果都为 false
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享