Javascript 里的类型转换是一个你永远绕不开的话题,无论你是在面试中仍是工做写代码,总会碰到这类问题和各类的坑,因此不学好这个那是不行滴。关于类型转换我也看过很多的书和各类博客、帖子,也查过规范和作过各类测试,这里就谈谈个人总结和理解吧。javascript
首先,为了掌握好类型转换,咱们要理解一个重要的抽象操做:ToPrimitive
java
为何说这是个抽象操做呢?由于这是 Javascript 内部才会使用的操做,咱们不会显示调用到。当须要将对象转换为相应的基本类型值时,ToPrimitive
就会调用对象的内部方法 [[DefaultValue]]
来完成。git
ToPrimitive
操做接收两个参数,一个是 input 须要转换的值,第二个是可选参数 hint 表明指望的转换类型。而且在调用 [[DefaultValue]]
的时候 hint 会传递过去。github
这里咱们首先只须要知道 [[DefaultValue]]
会调用 valueOf()
和 toString()
来完成基本类型值的转换。可是请注意:valueOf()
和 toString()
的调用逻辑顺序并非固定的取决于 hint 参数,这个咱们下面会讲到。面试
JavaScript 中的类型转换老是返回基本类型值,如字符串、数字和布尔值,不会返回对象和函数。那么这也对应了三种抽象操做:ToString
、ToNumber
和 ToBoolean
,下面就来逐一说明。函数
var a = {}; console.log(String(a)); // 显式类型转换,输出为:"[object Object]"
以上代码咱们一般称为显示类型转换,这里面就包含 ToString
抽象操做,也就是把非字符串值转换为字符串的操做。测试
先来看看非对象的基本类型值的 ToString
转换规则:es5
输入类型 | 输出结果 |
---|---|
Undefined | "undefined" |
Null | "null" |
Boolean | 输入 true ,输出 "true" 输入 false ,输出 "false" |
Number | 输入 NaN ,输出 "NaN" 输入 +0 或 -0 ,输出 "0" 若是输入小于 0 的数字,如: -2 ,输出将包括负号:"-2" 输入 Infinity ,输出 "Infinity" |
接着咱们重点来看一下输入是对象的转换规则。spa
这个时候 ToPrimitive
就出场了,而且 hint 参数是 String。还记得 ToPrimitive
内部是调用的[[DefaultValue]]
吗,而且这个时候 hint 是 String 。下面来看下这种状况下 ToPrimitive
的调用逻辑:prototype
[[DefaultValue]] 根据 hint 是 String 执行如下调用顺序:
toString()
并返回一个基本类型值,即返回这个值toString()
不存在或返回的不是一个基本类型值,就调用 valueOf()
valueOf()
存在并返回一个基本类型值,即返回这个值valueOf()
不存在或返回的不是一个基本类型值,则抛出 TypeError 异常那么这里就能够总结为:对象在类型转换为字符串时, toString()
的调用顺序在 valueOf()
以前,而且这两个方法若是都没有返回一个基本类型值,则抛出异常;若是返回了基本类型值 primValue,则返回 String(primValue)
基本类型值的 ToString 结果参看前面那个表格
咱们来测试一下。先看下这节开头的例子:
var a = {}; console.log(String(a));
字面量对象的原型是 Object.prototype
,Object.prototype.toString()
返回内部属性 [[Class]] 的值,那么结果就是 [object Object]
。OK 没有问题
而后测试一下 ToPrimitive
的调用逻辑。来看下这段代码:
var a = Object.create(null);
上面的意思是建立一个没有原型的对象(没有原型就没有继承的 toString()
和 valueOf()
了)。接下来:
console.log(String(a)); // Uncaught TypeError: Cannot convert object to primitive value
这里由于没有 toString()
和 valueOf()
因此就抛出 TypeError 异常了。OK,跟前面的总结一致。
下面来测试一下 toString()
和 valueOf()
的调用顺序逻辑,上代码:
a.toString = function() { return 'hello'; }; a.valueOf = function() { return true; }; console.log(String(a)); // "hello"
咱们加入了 toString()
和 valueOf()
,而且跟前面的总结一致,确实是 toString() 先返回结果。接着作一下变化:
a.toString = function() { return {}; }; // 或是直接去掉这个方法,a.toString = undefined; a.valueOf = function() { return true; }; console.log(String(a)); // "true"
当 toString() 返回的不是一个基本类型值或不存在 toString() 时,返回 valueOf() 的结果,而且遵循基本类型值的 ToString 转换结果。OK,验证没有问题 其余的状况也能够根据前面的总结逻辑本身验证下。
在《Javascript 高级程序设计(第 3 版)》和《你不知道的 Javascript(中卷)》上均未提到类型转换到字符串会与 valueOf() 有关系
首先照例先来看下非对象的基本类型值的 ToNumber
转换规则:
输入类型 | 输出结果 |
---|---|
Undefined | NaN |
Null | 0 |
Boolean | 输入 true ,输出 1 输入 false ,输出 0 |
Number | 输入 "" ,输出 0 输入 "Infinity" ,输出 Infinity 输入有效数字的字符串(包括2、八和十六进制),输出数字的十进制数值 若是输入包含非数字格式的字符串,输出 NaN |
字符串转数字上面只说了一些经常使用的状况,更多细节请看 这里
而后来看看对象 ToNumber
的状况。这里与对象转字符串的状况相似,也会调用 ToPrimitive
来转换(hint 是 Number)。但细节与 ToString
稍有不一样,这里直接给出结论:
对象在类型转换为数字时, valueOf()
的调用顺序在 toString()
以前,而且这两个方法若是都没有返回一个基本类型值,则抛出异常;若是返回了基本类型值 primValue,则返回 Number(primValue)
这里验证了 ToPrimitive 里面说到的,[[DefaultValue]] 会根据 hint 参数决定 toString() 和 valueOf() 的调用顺序
接着来用代码说话:
var a = Object.create(null); console.log(Number(a)); // Uncaught TypeError: Cannot convert object to primitive value
这里由于没有 toString()
和 valueOf()
因此就抛出 TypeError 异常了。OK,跟前面的总结一致。
咱们先加入 valueOf() 方法:
a.valueOf = function() { return 123; } console.log(Number(a)); // 123
valueOf() 返回了数字 123,因此输出没问题。再修改一下:
a.valueOf = function() { return true; } console.log(Number(a)); // 1
valueOf() 返回了 true,这也是一个基本类型,而后根据基本类型转换规则 true 转换为 1,也是对的。
再来:
a.valueOf = function() { return NaN; } console.log(Number(a)); // NaN
NaN 是一个特殊的数值,因此也是基本类型。OK,也是对的。
这里的结果说明了《Javascript 高级程序设计(第 3 版)》关于对象转换为数字的解释是有错误的,书上是这么说的:若是转换的结果是 NaN,则调用对象的 toString() 方法
再来验证一下 toString() 的调用顺序:
a.valueOf = function() { return {}; } a.toString = function() { return '123'; } console.log(Number(a)); // 123
由于 valueOf() 返回了对象非基本类型值,转而执行 toString(),返回的 "123" 根据字符串转换数字的规则就是 123,对于 valueOf() 和 toString() 的执行顺序验证也是 OK 的。
最后咱们来看看转换为布尔值。这个比较简单,一个列表能够所有概括了:
输入类型 | 输出结果 |
---|---|
Undefined | false |
Null | false |
Number | 输入 +0,-0,NaN ,输出 false 输入其余数字,输出 true |
String | 输入 length 为 0 的字符串(如:"" ),输出 false 输入其余字符串,输出 true |
Object | 输入任何对象类型,输出 true |
ToBoolean 转换规则比较简单,只有一个须要注意的地方,那就是封箱操做:
var a = new Boolean(false); console.log(Boolean(a)); // 输出是 true 不是 false 喔
new Boolean(false)
返回的是对象不是布尔值,因此最好避免进行相似的操做。
以上便是我总结的 JS 类型转换基本规则,当你明显感知类型转换即将发生时能够拿上面的规则去套(也就是咱们一般说的显式类型转换,以上转换规则面试时特别有用喔)。
既然规则有了,下一篇准备聊一下隐式类型转换,有了这篇的基础掌握隐式转换会容易不少。
欢迎 star 和关注个人 JS 博客:小声比比 Javascript