Skip to content

JavaScript 抽象类型比较(==)详解

在 JavaScript 中,抽象相等比较(==)和严格相等比较(===)在使用时可能返回值不一致。== 在比较时会执行内部的类型转换规则,被称为 抽象类型比较算法

本文将从“相同类型”与“不同类型”开始介绍,整理了 == 的转换行为。

一、相同类型的比较

当两边的值类型相同时,抽象比较不会进行类型转换,直接比较它们的值(或引用)。

1. 普通值类型(number, string, boolean, bigint, symbol)

直接比较值是否相等:

js
1 == 1          // true
'abc' == 'abc'  // true
true == true    // true

2. 引用类型(如对象、数组、函数)

比较的是内存引用地址是否一致

js
const a = { x: 1 }
const b = a
const c = { x: 1 }

a == b  // true,同一个引用
a == c  // false,不同引用

3. NaN 特例

不管 NaN == NaN 还是 NaN === NaN,结果都为 false

js
NaN == NaN    // false

二、不同类型的比较(抽象转换规则)

当比较的两边类型不同时,JavaScript 会进行内部的类型转换步骤,再去进行比较。

转换原则:

  • 如果其中一边是对象,则尝试将其转换为原始类型。
  • 普通类型之间,尝试转换为 number 再比较。

1. null 和 undefined

这是一个特例:

js
null == undefined  // true

这两个是唯一一对类型不同但被认为相等的值。

2. 原始类型之间(string, number, boolean)

当两边是不同的原始类型,通常都会先转换为 number 再比较:

js
'1' == 1       // true -> '1' 转成 1
true == 1      // true -> true 转成 1
false == 0     // true -> false 转成 0
'0' == false   // true -> '0' -> 0, false -> 0

3. 对象与原始值之间的比较

对象参与比较时,JavaScript 会尝试将对象转换为原始值。这依赖于对象的 valueOf()toString() 方法。

转换步骤:

  1. 优先调用 obj.valueOf(),如果返回原始类型,就用此值比较
  2. 否则再调用 obj.toString(),如果返回原始类型,就用此值比较
  3. 如果两者都未返回原始值,则抛出错误。
js
const obj = {
  valueOf() {
    return 42
  }
}

obj == 42    // true -> valueOf() 返回原始值 42,类型一致,直接比较

如果 valueOf() 返回的是对象,则继续尝试调用 toString()

js
const obj = {
  valueOf() {
    return {}
  },
  toString() {
    return '123'
  }
}

obj == 123  // true -> toString() 返回 '123',转成数字 123,再比较

比较的两个值如果类型仍不一致,会再转为 number 比较:

js
{} == '[object Object]'     // true
{} == 0                     // false(对象转为 NaN,由于NaN不等于任何值,所以返回false)

4. Date 对象的例外

在大多数对象中,valueOf() 优先于 toString(),但 Date 是例外

Date 优先调用 toString() 而不是 valueOf()

js
const d = new Date('2025-05-25T14:42:31')
console.log(d.valueOf())         // 返回时间戳
console.log(d.toString())        // 返回格式化时间字符串

d == d.toString()     // true

三、总结

情况行为说明
相同类型原始值直接比较值是否相等
相同类型对象比较引用是否相等
任一值为 NaN返回 false,NaN和任何值都不相等
null == undefinedtrue(特例)
不同类型原始值转换为 number 后比较
对象与原始值对象 → valueOf() → toString() → 转 number 后比较
Date 对象优先调用 toString() 再比较

四、建议

  1. 应该避免使用 ==,优先使用 ===,可以减少类型转换带来的意外。
  2. 如果确实需要 ==,一定要先清楚转换规则,避免代码出现意外。

五、对象转布尔与相等比较的区别(易混淆点)

JavaScript 中,任何非 nullundefined0NaN''false 的值在布尔上下文中都被认为是 真值(truthy)。但对象即使“包装了 false”,也被认为是 truthy。

js
Boolean(new Boolean(false))  // true
new Boolean(false) == false  // true

解释:

  • Boolean(new Boolean(false)) 的逻辑是:传入一个对象 → 对象是 truthy → 返回 true
  • new Boolean(false) == false 的逻辑是:
    • 左边是对象,触发抽象相等比较
    • 调用 valueOf() 得到原始值:false
    • false == false,返回 true

说明了:

  • Boolean() 是显式转换为布尔值,不涉及抽象相等比较
  • == 则是试图把对象“解包”成原始值,再进行比较