细说 JS 中的 “==” 运算符

264 阅读3分钟

前言

判断两个变量是否相等是编程中最重要的操作之一。许多程序员为了省事,用的都是 == 操作符。

这并不是一个好习惯,可能导致难以检测的 bug,但我们不可避免地会接触到这类代码,面试中也经常出这类题目。

来做几道题,看看你究竟是否掌握了抽象比较 == 的类型转化规则

题目

你能自信的做出下面10道题吗?

// 第一题
console.log(1 == '1a')
// 第二题
console.log(.1 == '.1')
// 第三题
console.log([] == false)
// 第四题
console.log({} == '')
// 第五题
console.log({} == '[object Object]')
// 第六题
const obj = {
    toString() {
      return 1
    },
}
console.log(obj == 1)
// 第七题
const obj = {
    valueOf() {
      return 0
    },
    toString() {
      return '1'
    },
}
console.log(obj == '1')
// 第八题
const obj = {
    valueOf() {},
}
console.log(obj == undefined)
// 第九题
const obj1 = {
    valueOf() {
      return 0
    },
}
const obj2 = Object.create(obj1)
obj2.toString = () => '1'
console.log(obj2 == 1)
// 第十题
const obj = {
  toString() {
    return {
      valueOf() {
        return 1
      },
      toString() {
        return 0
      },
    }
  },
}
console.log(obj == 0)

规则总结(省事看这里)

进行 == 比较时,从上到下匹配这些规则。

  • 如果操作数具有相同的类型,则将它们进行如下比较:

    • 基础数据类型:仅当两个操作数具有相同的字面量时才返回 true

    • 引用数据类型:仅当两个操作数都引用同一个对象时才返回 true

    • 0 +0 -0 视为相同的值,NaNNaN 不相等。

  • 如果一个操作数是 nullundefined,另一个操作数也是 nullundefined ,则返回 true,否则返回 false

  • 如果两个操作数是不同类型的,则会尝试将它们转换为相同类型:

    • 如果操作数中有 Boolean,先将 true 转换为 1false 转化为 0

    • 当数字与字符串或 BigInt 进行比较时,会使用 Number(xxx) 的方式将字符串转化为数字,可能存在精度缺失。

    • 如果操作数之一是引用数据类型,另一个是基础数据类型,按以下规则进行转换:

      • 会先调用对象身上的 valueOf() 方法,如果得到的是基础数据类型,则使用此结果与另一操作数比较。

      • 如果对象的 valueOf() 方法没有返回基础数据类型,再调用对象的 toString() 方法,如果得到的是基础数据类型,则使用此结果与另一操作数比较;如果还是引用数据类型,抛出 TypeError: Cannot convert object to primitive value

      • 所有对象原生的 valueOf() 方法都是返回对象自己本身。

      • 大部分对象原生的 toString() 方法返回的是 '[object XXX]' 类型的字符串,一部分对象重写了此方法,比如数组的 toString() 方法,返回的是数据调用 join 方法的结果。

答案解析

接下来公布答案:

false
true
true
false
true

true
false
false
false
// TypeError: Cannot convert object to primitive value

你做对了吗?

还不懂的话,来看看解析:

  • 第一题:数字与字符串比较,使用 Number('1a') 转换字符串返回 NaN,与1 不相等。

  • 第二题:字面量 .1 是数字 0.1,使用 Number('.1') 返回也是 0.1,相等。

  • 第三题:先将 false 转化为 0,然后使用 []toString 方法,等同于 [].join() 返回 '',然后使用 Number('') 返回 0,相等。

  • 第四题:{}toString 方法返回 '[object Object]',与 '' 不相等。

  • 第五题:{}toString 方法返回 '[object Object]',相等。

  • 第六题:objtoString 方法返回 1,相等。

  • 第七题:优先调用 objvalueOf 方法返回 0,不相等。

  • 第八题:其中有一操作数是 undefined,另一操作数不是 nullundefined,直接返回 false

  • 第九题:通过 Object.createobj1 为原型创建 obj2,比较时优先调用 obj2valueOf 方法返回 0,即使是在原型链上,不相等。

  • 第十题:objvalueOf 方法与 toString 方法返回的都不是基础数据类型,报错 TypeError: Cannot convert object to primitive value

结语

如果文中有错误或不严谨的地方,请务必给予指正,十分感谢。

如果喜欢或者有所启发,欢迎点赞关注,鼓励一下新人作者。