== 与 === 的区别及隐式转换

1. 核心区别

  • ==:会进行隐式类型转换 (Coercion),尝试将两个值转换为相同类型后再比较。
  • ===:如果类型不同,直接返回 false。如果类型相同,再比较值。永远推荐使用 ===

2. 隐式类型转换规则 (面试考点)

(1) null 和 undefined

null == undefined // true
null === undefined // false

// 面试题:判断空值
if (x == null) // 等价于 if (x === null || x === undefined)

(2) 字符串 vs 数字

如果有一个是数字,另一个也会被转换为数字

'1' == 1 // true -> Number('1') === 1
'1a' == 1 // false -> Number('1a') -> NaN !== 1

(3) 布尔值 vs 其他类型

如果有布尔值,布尔值先转为数字 (true -> 1, false -> 0)。

true == '1' // true -> 1 == Number('1') -> 1 === 1
true == '2' // false -> 1 == Number('2') -> 1 !== 2

(4) 对象 vs 原始值

如果有一个是对象,会先将对象转换为原始值

转换顺序:

  1. 调用 obj[Symbol.toPrimitive](hint)
  2. 调用 obj.valueOf()
  3. 调用 obj.toString()
[1, 2] == '1,2' // true -> [1,2].toString()

3. 经典面试题解析

(1)[] == ![] 为什么是 true?

步骤拆解:

  1. ![] 优先级更高,空数组转 Boolean 为 true,取反为 false。
  2. 比较变为:[] == false
  3. 有一方是 Boolean,转为数字:false -> 0
  4. 比较变为:[] == 0
  5. 有一方是对象,[] 转换为原始值:[].valueOf() -> [] (非原始值),继续 [].toString() -> "" (空字符串)。
  6. 比较变为:"" == 0
  7. 有一方是字符串,转为数字:Number("") -> 0
  8. 最终:0 === 0 -> false (等等,这里逻辑错了?)

修正逻辑

  1. ![] -> false
  2. [] == false
  3. Boolean 转 Number:[] == 0
  4. Object 转 Primitive:"" == 0
  5. String 转 Number:0 === 0 -> true

(2)obj.valueOftoString 哪个先执行?

如果是 == 比较,默认 hint 是 'default' (表现像 'number'),优先调用 valueOf。如果 valueOf 返回对象,再调用 toString。 Date 对象特殊,默认 hint 是 'string',优先 toString

4. 特殊值比较

  • NaN:与任何值都不相等,包括自己。
    NaN == NaN // false
    // 判断 NaN:Number.isNaN(val) 或 Object.is(val, NaN)
  • +0 vs -0
    +0 === -0 // true
    Object.is(+0, -0) // false

5. 面试加分项:Object.is()

Object.is(val1, val2)=== 基本一致,只有两个区别:

  1. +0 不等于 -0
  2. NaN 等于 NaN