主题
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()
方法。
转换步骤:
- 优先调用
obj.valueOf()
,如果返回原始类型,就用此值比较 - 否则再调用
obj.toString()
,如果返回原始类型,就用此值比较 - 如果两者都未返回原始值,则抛出错误。
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 == undefined | true(特例) |
不同类型原始值 | 转换为 number 后比较 |
对象与原始值 | 对象 → valueOf() → toString() → 转 number 后比较 |
Date 对象 | 优先调用 toString() 再比较 |
四、建议
- 应该避免使用
==
,优先使用===
,可以减少类型转换带来的意外。 - 如果确实需要
==
,一定要先清楚转换规则,避免代码出现意外。
五、对象转布尔与相等比较的区别(易混淆点)
JavaScript 中,任何非 null
、undefined
、0
、NaN
、''
、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()
是显式转换为布尔值,不涉及抽象相等比较==
则是试图把对象“解包”成原始值,再进行比较