在 JS 中存在着原始类型和引用类型,通常在开发过程中都会有类型转换这样一个操作,当我们人为地进行类型转换时,称为显式类型转换,另外是 v8 引擎在执行某些操作时会自动发生类型转换,这种我们称之为隐式类型转换

类型转换

我们先来看下面一段代码;

if ({}) {
    console.log(1);
}

探究 v8 中的类型转换机制_类型转换

很明显,在上面的代码中,自动地发生了类型转换,因为if里面只能为布尔值,说明对象被转换成了布尔值,且还为true;那在 v8 中的类型转换机制是什么样的呢?

显式类型转换

在 JavaScript 中,显式类型转换通常是通过构造函数或者类型转换函数来完成的

原始值转原始值

使用构造函数:

  • Number() - 将给定值转换为数字。
  • String() - 将给定值转换为字符串。
  • Boolean() - 将给定值转换为布尔值。

我们可以去官方文档(Annotated ES5)查看这三种方法转换不同值的规则;

Number()

当我们给它传进去一个支持的值时,v8 就会自动地调用 ToNumber 方法,返回一个数字字面量;

探究 v8 中的类型转换机制_类型转换_02

undefined 会被转换成 NaNnull 被转换成 0,而布尔值true为1,false为0;如果是Number类型的话,则不发生类型转换

而当 StringNumber 时,空字符串转为 0都是数字的字符串直接转为数字,而数字和字母混杂的字符串则转为 NaN

String()

同样地,转为 String 类型时,也会遵循以下规则;

探究 v8 中的类型转换机制_Boo_03

可以看到,转成 String 非常的简单,都是直接加上引号,变成字符串,NumberString 也是如此;

探究 v8 中的类型转换机制_Boo_04

Boolean()

官方文档也给出了转 Boolean 的规则;

探究 v8 中的类型转换机制_字符串_05

可以看到,在有效数字中只有 0 会被转成 falseNaN 也会被转成 false,字符串中只有空字符串会被转成 falseundefinednull 都会被转成 false不传值的情况下,默认也是 false

隐式类型转换

隐式类型转换(也称为自动类型转换)是指在程序运行过程中,当一个表达式或操作涉及不同类型的值时,编程语言自动将一个或多个值转换为相同类型的过程

如果是原始值转原始值,同样遵循上面的规则;那如果对象转原始值是怎么转换的呢?

对象转原始值

在这之前,我们回顾一下,不同数据类型身上的 toString() 方法是什么样的;

toString()

  1. Object.prototype.toString() 返回一个形如 "[object XXXX]" 样式的字符串
  2. Array.prototype.toString() 返回一个由数组内部的元素以逗号拼接的字符串
  3. xxx.prototype.toString() 返回一个字符串字面量

对象转 Number

探究 v8 中的类型转换机制_类型转换_06

当我们给 Number() 构造函数传入一个对象时,会执行以下步骤;

  • 先调用 ToNumber 方法,当传入的是对象时,遵循上图规则
  • 调用 ToPrimitive 方法,得到返回值 primValue
  • 再把 primValue 返回到 ToNumber 方法中再执行一遍

那关于 ToPrimitive 方法会做一些什么操作呢?官方文档也给出了详细规则如下;

探究 v8 中的类型转换机制_Boo_07

也就是说 ToPrimitive 方法如果接收的参数是原始类型,ToPrimitive则不工作,不会发生转换,如果接收的是引用类型ToPrimitive 方法的目的就是把(引用类型)对象转换成原始值(原始类型)

关于 ToPrimitive 方法接收到对象且要转换成 Number 类型时,具体步骤如下;

ToPrimitive(obj, Number)

ToPrimitive 方法会做如下操作;

  1. 判断接收到的值是不是原始类型,是则返回
  2. 否则,调用valueOf方法,如果得到了原始值,则返回
  3. 否则,调用toString方法,如果得到了原始值,则返回
  4. 报错

ToPrimitive 方法,首先尝试调用对象的 .valueOf() 方法。如果 .valueOf() 返回了一个非对象值(即原始值),则直接返回这个值。如果 .valueOf() 返回的是一个对象值(即另一个对象),则尝试调用对象的 .toString() 方法。如果 .toString() 返回了一个非对象值,则返回这个值。

.valueOf 方法只能把包装类对象转成原始值;而.toString() 方法在多个构造函数的原型上都有,如果是对象,则会调用 Object.prototype.toString 方法,如果是数组,则调用 Array.prototype.toString 方法,.toString() 方法我们在上个章节已经详细介绍过了。

console.log(Number({}));

// Number({})
// 1. ToNumber({})
// 2. let primValue = ToPrimitive({},Number)
// 3. '[object Object]' // 字符串
// 4. ToNumber('[object Object]')  // 非数字字符串转 Number 为 NaN

探究 v8 中的类型转换机制_类型转换_08

对象转 String

知道了对象转 Number 的过程之后,其实对象转 String 也是类似的过程; 先调用ToString()方法,然后在该函数中会调用ToPrimitive()对象转为原始值返回给ToString()

ToPrimitive(obj, String)
  1. 判断接收到的值是不是原始类型,是则返回
  2. 否则,调用toString方法,如果得到了原始值,则返回
  3. 否则,调用valueOf方法,如果得到了原始值,则返回
  4. 报错

对象转 Boolean

关于对象转 Boolean 值,在官方文档中,有一个设定,那就是任何对象转布尔都是true

面试题:[] == ![]

在了解完类型转换后,我们来看一道经典的面试题;[] == ![];其中就涉及到了类型的转换;

[] == ![]
// 对象转 Boolean 是 true
// [] == !true
// [] == false  取反
// [] == 0  将 [] 转 Number
// Number([]) == 0
// Number('') == 0   空数组 .toString 方法转为了 ''
// 0 == 0 最后相等 输出 true

可以看到,这道面试题考察的就是我们对 JS 中类型转换的理解;