引言
如果要评选 JavaScript 中最让人抓狂的特性,隐式类型转换绝对榜上有名。你一定经历过这样的绝望时刻:明明看起来是 true,条件判断却走进了 false 的分支;明明是在做加法,结果却得到了字符串拼接。更别提那个堪称前端面试"经典刑具"的灵魂拷问:\[\] == !\[\] 为什么是 true?在无数次与 == 和 + 斗智斗勇后,我们往往只能无奈地默念"万能的 === 保平安"。但逃避并不能解决问题,今天,就让我们不再依赖玄学,彻底扒开 JavaScript 类型转换的底裤,看看这些离谱操作背后,究竟藏着怎样的运作逻辑。
类型转换
在讲类型转换之前我们先来看看 == 和 === 有啥区别。
== :在判断相等的过程中会发生隐式类型转换
显示类型转换:
- String(x) --> ToString(x)
- Number(x) --> ToNumber(x)
- Boolean(x) --> ToBoolean(x)
在写代码的时候我们会进行一些类型转换,如将转换成 String 类型
javascript
console.log(String()); // ''
console.log(String(undefined)); // 'undefind'
console.log(String(null)); // 'null'
console.log(String(true)); // 'true'
console.log(String(false)); // 'false'
console.log(String(0)); // '0'
console.log(String(NaN)); // 'NaN'
console.log(String(Infinity)); // 'Infinity'
然后我们来看一下输出结果:

那为什么会输出这些呢?在转换成 String 的类型时其实是调用了 ToString 这个官方定义的方法。所以我们直接去看一下官方文档里面的 ToString 里面的内容。

其中的 Object 转换我们等会再讲,我们继续来看转换成 Number
javascript
console.log(Number()); // 0
console.log(Number(undefined)); // NaN
console.log(Number(null)); // 0
console.log(Number(true)); // 1
console.log(Number(false)); // 0
console.log(Number('123')); // 123
console.log(Number('-123')); // -123
console.log(Number('000123')); // 123
console.log(Number('000123a')); // NaN
依旧看一下输出结果:

ok,输出这些还是官方定义的 ToNumber 提前定义好的,我们依旧可以去官方文档上寻找答案。

最后一个我们要转换成 Boolean 类型呢?看完上面的你应该已经知道怎么说了吧,肯定是调用 ToBoolean ,没错,就是调用 ToBoolean

我们还是看一下官方文档中的 ToBoolean:

引用类型转字符串
说到引用类型转字符串,前面我们不是跳过了吗?其实是因为它有点特殊。我们就用引用类型转字符串来先看看有什么特殊。
javascript
console.log(String({a:1}));
console.log(String([]));
我们先来看看这两个输出结果:

对象的输出结果是 object Object 数组的输出结果为 '' 。那为什么输出结果是这两个呢?其实是因为转换的过程中调用了ToPrimitive 这个方法。
完整的过程:
String(x) --> ToString(x) --> ToPrimitive(x,String)
而其中的 ToPrimitive 依旧是官方定义的,我们还是先看一下官方文档

而其中的引用类型有要去查看 8.12.8

那我直接用我们看的懂的方式来说明白这些,其实就是下面的这些步骤:
ToPrimitive({a:1},String)
css1. 调用 {a:1}.toString(),如果得到一个原始值,则返回 2. 调用 {a:1}.valueOf(),如果得到一个原始值,则返回 3. 否则,报错
中间我们又需要调用到 toString 和 valueOf 方法,我们来补充一下这两种我们能使用的方法吧。
toString 和 valueOf
toString:
- {}.toString() --> 返回 "object Object"
- \[\].toString() --> 返回 '' 返回数组内部的元素以逗号分隔的字符串
- xx.toString() --> 返回 "xx" 返回xx的字符串形式
valueOf:
valueOf() 只能将包装类的对象转换成原始值
现在我们应该知道怎么引用类型转字符串了,我们接着来聊引用类型转数字
引用类型转数字
javascript
console.log(Number({}));
console.log(Number([]));
我们还是先看输出结果:

其实引用类型转数字的步骤是和转字符串差不多的依旧是:
Number(x) --> ToNumber(x) --> ToPrimitive(x,Number)
但是其中的 ToPrimitive 有点区别,我们就来看看 ToPrimitive(x,Number) 的过程吧
ToPrimitive({a:1},Number)
css1. 调用 {a:1}.valueOf(),如果得到一个原始值,则返回 2. 调用 {a:1}.toString(),如果得到一个原始值,则返回 3. 否则,报错
其实就是把第一步和第二步换了一下顺序。
引用类型转布尔类型
这个我们只需要记住一句话:所有的引用类型转 布尔 都是 true
隐式类型转换
说到隐式类型转换其实我们只需要它是什么时候进行的进行了。
隐式类型转换几种法式:
- 四则运算: + - * / %
- 判断语句: if while == >= <= != > <
说到 + 这个有点特殊需要我们注意一下。
+作为一元运算符 (朝 number 转换)
+作为二元运算符 (朝字符串转换)
scssx + y 令 lprim 为 ToPrimitive(x) 的结果 令 rprim 为 ToPrimitive(y) 的结果 lprim + rprim 如果 lprim 或者 rprim 有一个是字符串,就将另一个也转成字符串进行拼接 否则全部转 number 进行相加
总结
JavaScript中==运算符、四则运算及判断语句会触发隐式类型转换,其中引用类型转换需遵循ToPrimitive规则:转字符串优先调用toString()再调用valueOf(),转数字则相反,且引用类型转布尔值恒为true;显式转换主要通过String、Number、Boolean函数进行,特别值得注意的是二元运算符+在拼接字符串时会将非字符串侧转为字符串,否则转为数字相加。