== 是 JavaScript 中的抽象相等比较运算符,它和 ===(严格相等)的区别在于:== 在比较两个值是否相等之前,会尝试将它们转换为相同的类型。而 === 则不会做任何类型转换,类型不同直接返回 false。
正因为有这种隐式类型转换,== 的行为时常会违反直觉,成为让开发者“踩坑”的重灾区。彻底搞清楚这套转换规则,不是为了让代码变得花哨,而是为了看懂别人的代码以及深谙其陷阱后坚定地选择避让。
转换规则速查表
ECMAScript 规范定义了完整的“抽象相等比较算法”(Abstract Equality Comparison),梳理成日常可用的形式如下:
| 比较双方的类型 | 转换规则 |
| --- | --- |
| 类型相同 | 直接使用 === 比较,不转换(注意:NaN !== NaN,+0 === -0)。 |
| null 与 undefined | null == undefined 为 true;与其他任何值比较均为 false。 |
| 一方为数字,一方为字符串 | 将字符串转换为数字后比较。 |
| 一方为布尔值 | 先将布尔值转换为数字(true → 1,false → 0),然后再按上述规则比较。 |
| 一方为对象,一方为数字/字符串 | 调用对象的 valueOf() / toString() 取得原始值后再按上述规则比较。 |
| 其他组合 | 均返回 false。 |
注意:
BigInt与==的交互有额外规则,本书基于 ES6+ 核心语法,暂不展开。
一步一步推演几个典型案例
光看表格不够直观,我们拿几个常被问到甚至让面试官“自相残杀”的案例走一遍过程。
案例 1:0 == false 为什么是 true?
- 双方类型不同:
false是布尔值,0是数字。 - 规则:“一方为布尔值” → 将布尔值转换为数字,
false变成0。 - 现在变成
0 == 0,类型相同,严格相等,结果为true。
同理:1 == true 也是 true(true → 1),但 2 == true 是 false(2 == 1 不成立)。
案例 2:'' == 0 为什么是 true?
- 类型:空字符串是字符串,
0是数字。 - 规则:“一方为数字,一方为字符串” → 将字符串转换为数字,
''转换为0。 - 变成
0 == 0,结果为true。
同理:'123' == 123 成立,'abc' == NaN 不成立(NaN == 任何值 都是 false)。
案例 3:[] == 0 为什么是 true?(经典烧脑题)
- 类型:
[]是对象,0是数字。 - 规则:“一方为对象,一方为数字” → 需要先将对象转为原始值。
- 数组
[]的转换:先调用valueOf(),得到[]本身(不是原始值),于是再调用toString(),得到''(空字符串)。 - 现在变成了
'' == 0,正巧是我们刚分析过的案例 2 —— 结果为true。
由此可以推演出另一个著名等式:[] == false 为 true(false → 0,[] → '' → 0,0 == 0)。
案例 4:[] == ![] 为什么是 true?(绝对反直觉)
![]先被计算:[]是真值(truthy),![]就是false。- 现在变成
[] == false,与上一例相同,结果为true。
这完全是两套规则组合出的“彗星撞地球”式巧合,也充分展示了串联转换有多么不可预测。
案例 5:null == undefined 和 null == 0
null == undefined:符合特殊规则,直接返回true。null == 0:null仅与undefined相等,与其他任何值比较都返回false。
很多开发者在后端语言中习惯了 null == 0 为 true,在 JavaScript 中要特别留神。
常见易错点汇总
- 布尔值的转换优先级很高:一旦有布尔值参与,会先将其转为数字,而不是期望的“把另一方转为布尔”。因此
'false' == false是false('false'是非空字符串转为 NaN,false转为0,NaN ≠ 0)。 - 对象转原始值可能产生意外空白:对象 → 空字符串 → 0 这条转换链是很多诡异结果的根源。
NaN是唯一的“自己不等于自己”的值,NaN == NaN为false。- 不要期望
==用于与null或undefined的宽松比较能解决所有空值问题。x == null看似能同时检测null和undefined,一旦项目中混入其他假值(0、''、false),逻辑就可能悄悄崩坏。
最佳实践
在日常开发中,强烈推荐统一使用 === 进行相等判断。它简单、明确、没有暗箱操作,能把潜在的隐式转换问题挡在门外。唯一的例外是明确知道自己在做什么,并且代码上下文极度清晰(例如有意使用 x == null 来一次性排除 null 和 undefined),即便如此也应在代码中加上清晰的注释。
记住一句话:能不用 == 就不用 ==,代码的可读性和可维护性远胜于节省几次键盘敲击。如果你以后看到其他人写的 ==,就用这节的规则去分析,你会逐渐发现,多数“巧妙”的用法最终都演变成了技术债务。