在 JS 中存在着原始类型和引用类型,通常在开发过程中都会有类型转换这样一个操作,当我们人为地进行类型转换时,称为显式类型转换,另外是 v8 引擎在执行某些操作时会自动发生类型转换,这种我们称之为隐式类型转换。
类型转换
我们先来看下面一段代码;
if ({}) {
console.log(1);
}
很明显,在上面的代码中,自动地发生了类型转换,因为if
里面只能为布尔值,说明对象被转换成了布尔值,且还为true
;那在 v8
中的类型转换机制是什么样的呢?
显式类型转换
在 JavaScript 中,显式类型转换通常是通过构造函数或者类型转换函数来完成的。
原始值转原始值
使用构造函数:
Number()
- 将给定值转换为数字。String()
- 将给定值转换为字符串。Boolean()
- 将给定值转换为布尔值。
我们可以去官方文档(Annotated ES5)查看这三种方法转换不同值的规则;
Number()
当我们给它传进去一个支持的值时,v8
就会自动地调用 ToNumber
方法,返回一个数字字面量;
undefined
会被转换成 NaN
,null
被转换成 0
,而布尔值true
为1,false
为0;如果是Number
类型的话,则不发生类型转换。
而当 String
转 Number
时,空字符串转为 0,都是数字的字符串直接转为数字,而数字和字母混杂的字符串则转为 NaN
。
String()
同样地,转为 String
类型时,也会遵循以下规则;
可以看到,转成 String
非常的简单,都是直接加上引号,变成字符串,Number
转 String
也是如此;
Boolean()
官方文档也给出了转 Boolean
的规则;
可以看到,在有效数字中只有 0 会被转成 false
,NaN
也会被转成 false
,字符串中只有空字符串会被转成 false
,undefined
和 null
都会被转成 false
,不传值的情况下,默认也是 false
。
隐式类型转换
隐式类型转换(也称为自动类型转换)是指在程序运行过程中,当一个表达式或操作涉及不同类型的值时,编程语言自动将一个或多个值转换为相同类型的过程。
如果是原始值转原始值,同样遵循上面的规则;那如果对象转原始值是怎么转换的呢?
对象转原始值
在这之前,我们回顾一下,不同数据类型身上的 toString()
方法是什么样的;
toString()
Object.prototype.toString()
返回一个形如 "[object XXXX]
" 样式的字符串Array.prototype.toString()
返回一个由数组内部的元素以逗号拼接的字符串xxx.prototype.toString()
返回一个字符串字面量
对象转 Number
当我们给 Number()
构造函数传入一个对象时,会执行以下步骤;
- 先调用
ToNumber
方法,当传入的是对象时,遵循上图规则 - 调用
ToPrimitive
方法,得到返回值primValue
- 再把
primValue
返回到ToNumber
方法中再执行一遍
那关于 ToPrimitive
方法会做一些什么操作呢?官方文档也给出了详细规则如下;
也就是说 ToPrimitive
方法如果接收的参数是原始类型,ToPrimitive
则不工作,不会发生转换,如果接收的是引用类型,ToPrimitive
方法的目的就是把(引用类型)对象转换成原始值(原始类型)。
关于 ToPrimitive
方法接收到对象且要转换成 Number
类型时,具体步骤如下;
ToPrimitive(obj, Number)
ToPrimitive
方法会做如下操作;
- 判断接收到的值是不是原始类型,是则返回
- 否则,调用
valueOf
方法,如果得到了原始值,则返回 - 否则,调用
toString
方法,如果得到了原始值,则返回 - 报错
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
对象转 String
知道了对象转 Number
的过程之后,其实对象转 String
也是类似的过程; 先调用ToString()
方法,然后在该函数中会调用ToPrimitive()
将对象转为原始值返回给ToString()
。
ToPrimitive(obj, String)
- 判断接收到的值是不是原始类型,是则返回
- 否则,调用
toString
方法,如果得到了原始值,则返回 - 否则,调用
valueOf
方法,如果得到了原始值,则返回 - 报错
对象转 Boolean
关于对象转 Boolean
值,在官方文档中,有一个设定,那就是任何对象转布尔都是true。
面试题:[] == ![]
在了解完类型转换后,我们来看一道经典的面试题;[] == ![]
;其中就涉及到了类型的转换;
[] == ![]
// 对象转 Boolean 是 true
// [] == !true
// [] == false 取反
// [] == 0 将 [] 转 Number
// Number([]) == 0
// Number('') == 0 空数组 .toString 方法转为了 ''
// 0 == 0 最后相等 输出 true
可以看到,这道面试题考察的就是我们对 JS 中类型转换的理解;