从 == 到 {} + []:一文拆解 JavaScript 类型转换的隐形魔法

77 阅读3分钟

前言

let a = 123
console.log(a)
console.log(1 == '1')
console.log(1 === '1')

我们知道 = 是赋值,右边赋给左边,而 ===== 都是判断左右两边是否相等的运算符,但是它们又有什么差别呢?我们输出上面结果试试
4e43cb83-5667-4cfe-a1a1-d21cbc3261ac.png
诶,为啥 == 返回的是 true=== 返回而是 false ,按照我们之前所理解的,都应该返回 false 才对,怎么不同类型的放一起比较还返回 true 呢?那是因为 == 在判断值的过程中会发生隐式类型转换,把左右两边转换成同种类型进行比较,而 === 不进行类型转换,只有当类型相同值也相同时才会返回 true 。什么是类型转换呢?今天带大家一探究竟

类型转换

无论是显式转换还是隐式转换都存在 原始 -> 原始, 引用 -> 原始

一、显式类型转换

顾名思义,你写的代码里“明明白白”地调用了某种转换手段,一看就知道“这里要把某值变成某类型”。我们用:Number / String / Boolean。这三个函数就是 JS 暴露给我们的“遥控器”:

遥控器官方对应算法一句话记忆
Number(x)ToNumber(x)把 x 榨成数字
String(x)ToString(x)把 x 榨成字符串
Boolean(x)ToBoolean(x)把 x 榨成布尔

注意: ToNumber/ToString/ToBooleanECMAScript 规范里的抽象操作,代码里写不出来,但所有隐式转换最终都会调到它们。

let num = -1
let str = 'hello'  
let flag = true
console.log(String(num));
console.log(String(flag));
console.log(Number(str));   
console.log(Number(flag));
console.log(Boolean(num));
console.log(Boolean(str));

7701ded7-ad05-4fb4-9b2e-13bfac0f4346.png
这里面的 NAN 是 not a number 的意思,true 则是代表 1 ,false 则是代表 0。

二、隐式类型转换

引用类型转原始值(v8自发的执行)通常发生在隐式转换过程中

  • 转布尔 --- 所有的引用类型转布尔都是 true
  • 转数字
    1. Number(x)
    2. ToNumber(ToPrimitive(x, Number))
    • ToPrimitive:
    1. 调用 valueOf() 方法,如果得到原始值,则返回
    2. 否则,调用 toString() 方法,如果得到原始值,则返回
    3. 否则,报错
  • 转字符串
    1. String(x)
    2. ToString(ToPrimitive(x, String))
    • ToPrimitive:
    1. 调用 toString() 方法,如果得到原始值,则返回
    2. 否则,调用 valueOf() 方法,如果得到原始值,则返回
    3. 否则,报错

valueOf()

valueOf() 只能将包装类变成原始类型,比如 new 调用得到的实例对象

let a = new Number(123)
console.log(a.valueOf())
let b = new Boolean(234)
console.log(b.valueOf())
let c = new String('hello')
console.log(c.valueOf())

1a810d22-7032-4667-9b7a-0d0260e6fff1.png

toString()

  1. {}.toString() // '[object Object]'
  2. [].toString() // 返回由数组内部元素以逗号拼接的字符串
  3. 其他.toString() // 将值用引号引起来

什么情况下会发生隐式类型转换

  1. 四则运算 + - * / % 默认往Number上转
  2. 判断语句 if while == >= <= > < !=

注意: +

  1. 作为一元运算符 会直接调用 ToNumber(x) (Number('123') === +('123'))
  2. 作为二元运算符
    val1 + val2
    lprim = ToPrimitive(val1) rprim = ToPrimitive(val2)
    lprim + rprim
  3. 如果 lprim 或者 rprim 有一个是字符串,另一个直接被 ToString()
  4. 否则 全部 ToNumber

结尾彩蛋

[] == ![]  // true
// [] == !true
// [] == false
// [] == 0
// '' == 0
// 0 == 0
{} + []  ==>在Windows中被当成代码块+数组,于是只剩  +[]  →  0 (经典面试坑)(node不一样)
[] + {}  ==>等价于  "" + "[object Object]""[object Object]" 

结语

今天我们沿着 1 == '1' 这条裂缝,一路撬开了 JS 的“类型转换黑盒”:

  • === 是严格的“同类型+同值”守门员; == 则是会隐式施法的“类型橡皮擦”。
  • 显式转换 Number() / String() / Boolean() 让你对结果一目了然;隐式转换 ToPrimitive 则在运算符背后悄悄调用 valueOf → toString 双通道。
  • 二元 + 只要遇到字符串就立刻化身“拼接怪”;而 {} + [] 的零结果,只是解析器把 {} 当成代码块的小彩蛋。

记住口诀: “业务写代码用 ===,面试刷题防隐式;对象先 valueOftoString,括号一加魔法失效。” 下次再看到 [] == ![] 返回 true ,你就能笑着把转换路径一步步剥给对方听——真正的“魔法”,不过是规范里写好的算法罢了。