JavaScript
隐式转换 深度解析
四种类型转换
在进行 == 或 + 运算之前,JavaScript 引擎会先对操作数执行类型转换。理解这四种基础转换是掌握隐式转换的关键。
ToPrimitive
ToNumber
ToString
ToBoolean
假值军团 — Falsy Values
只有以下 8 个 值转换为 false,其他一切都是 true
常见陷阱:"0"、[]、{}、function(){} 都是 truthy!
== 比较算法
==(宽松相等)的执行逻辑由 ECMAScript 规范定义。下面是简化后的核心步骤,按优先级排序:
类型相同?→ 直接用 ===
如果两个操作数的类型相同,== 的行为与 === 完全一致,不会进行任何转换。
1 === 1 // true
"hi" === "hi" // true
NaN === NaN // false ← NaN 不等于自身!null 和 undefined 互相相等
这是规范中的特例:null 和 undefined 用 == 比较时返回 true,且不与其他任何值相等。
null == undefined // true
null == 0 // false
undefined == "" // false
null == null // true数字 vs 字符串 → 转字符串为数字
如果一边是数字,另一边是字符串,会将字符串通过 ToNumber() 转换后再比较。
0 == "" // ToNumber("") → 0, 0 == 0 → true
0 == "0" // ToNumber("0") → 0, 0 == 0 → true
1 == "1" // ToNumber("1") → 1, 1 == 1 → true
NaN == "NaN" // ToNumber("NaN") → NaN, NaN == NaN → false布尔值参与?→ 先转成数字
如果有一边是布尔值,会先将其转为数字(true → 1、false → 0),然后再按后续规则继续比较。
true == 1 // true → 1, 1 == 1 → true
false == 0 // false → 0, 0 == 0 → true
true == "1" // true → 1, "1" → 1, 1 == 1 → true
false == "" // false → 0, "" → 0, 0 == 0 → true
false == "0" // false → 0, "0" → 0, 0 == 0 → true关键记忆:布尔值在这一步永远先变成 0 或 1,然后走数字比较路线。 所以 true == "1" 为 true,但 true === "1" 为 false。
对象 vs 原始值 → 对象执行 ToPrimitive
如果一边是对象,另一边是原始值,会对对象执行 ToPrimitive()(默认 hint 为 "number"),然后继续比较。
对于普通对象,valueOf() 返回对象自身(不是原始值),所以会继续调用 toString()。
// 数组的 toString 特殊行为
[].toString() // "" (空数组 → 空字符串)
[1, 2].toString() // "1,2" (逗号连接)
[null].toString() // "" (null → "")
// 普通对象
{}.toString() // "[object Object]"都不匹配?→ 返回 false
如果经过以上所有规则都无法匹配(例如 Symbol 类型参与比较),则直接返回 false。
Symbol("a") == Symbol("a") // false (不同 Symbol)
0 == Symbol("0") // TypeError! (不能转为数字)为什么 [] == ![] 为 true?
[] == ![]先计算右侧 ![]
![]→ false[] 是对象,truthy → 取反 → false
[] == false规则④:布尔值参与 → 转为数字
[] == ToNumber(false)→ [] == 0false → 0
ToPrimitive([]) == 0[].valueOf() → [] (非原始) → [].toString()
"" == 0[].toString() → ""(空字符串)
ToNumber("") == 0→ 0 == 0"" → 0
0 == 0→ ✅ true类型相同,直接比较
+ 运算符的双面性
+ 是 JavaScript 中最特殊的运算符 —— 它既能做数学加法,也能做字符串拼接。 核心规则只有一条:任一操作数是字符串就拼接,否则做加法。
执行流程
ToPrimitive 两个操作数
对象先转原始值(valueOf → toString)
有字符串吗?
任一侧是字符串 → 字符串拼接
否则 → ToNumber 后数学相加
1 + "2"→ "12""hi" + true→ "hitrue""a" + null→ "anull""" + []→ ""1 + 2→ 3true + true→ 2null + 1→ 1false + 0→ 0对比:- 运算符永远是数学运算
"5" - 2→ 3字符串转数字后减
"5" + 2→ "52"字符串拼接
"abc" - 1→ NaN"abc" → NaN
那些令人困惑的陷阱
[] == ![]
true![] 先求值为 false,然后 [] == false,布尔转数字 → [] == 0,对象 ToPrimitive([]) → "",字符串转数字 → 0 == 0 → true
"b" + "a" + + "a" + "a"
"baNaNa"一元 + 将 "a" 转为数字 → NaN,然后字符串拼接:"" + "a" + NaN + "a" → "baNaNa"
null + 1 null + 0
1 / 0null 通过 ToPrimitive 仍是 null,+ 右侧不是字符串,ToNumber(null) → 0,所以 0+1=1,0+0=0。
undefined + 1 undefined == null
NaN / trueToNumber(undefined) → NaN,NaN + 1 = NaN。而 undefined == null 是规范特例,直接返回 true。
一图掌握核心规则
null == undefinedtrueNaN == NaNfalse0 == ""true0 == "0"true"" == "0"falsefalse == "0"truefalse == nullfalse"\t" == 0true[] == 0true[1] == 1true"" == []true0 == []true终极建议
在实际开发中,始终使用 ===(严格相等)进行比较。隐式转换是 JavaScript 的历史包袱,理解它的机制是为了更好地避开陷阱,而不是利用它写出令人困惑的代码。