引言
面试当中我们常会碰到一些奇奇怪怪的判断语句,隐藏在其中的类型转换可能会惹得许多小白困扰不已,接下来就由小编来讲清楚js中神鬼莫测的类型转换,拨开大家心底的迷雾。
首先来一段代码开开胃,说明打印结果的原因:
console.log(1 == '1'); // true
console.log(1 === '1'); // false
let a = []
let b = []
console.log([] == []); // false
先要聊一聊‘==’
和‘===’
的区别:两个等号只判断两边变量的值是否相等,不在乎类型;三个等号要判断是否全等,值和类型都要相等。因为js引擎在用‘==’
判断是否相等时会发生隐式类型转换,而‘===’
判断时不会。
第6行打印false是因为js引擎在判断两边变量是否相等时只会读取栈里面的值,栈里变量a和b的值是堆中的两个不同的地址
显式类型转换
原始值转原始值
1. 原始值转布尔
- 直接调用布尔的构造函数
Boolean()
,传入原始值作为参数- 原始值为数值类型,只有
0
和NAN
会被转换为false - 原始值为字符串类型,只有空字符串''会被转换为false
- 原始值为
undefined
和null
都会被转换为false
- 原始值为数值类型,只有
2. 原始值转数字
-
直接调用
Number()
,传入原始值作为参数- 原始值为字符串类型,若所有字符都为数字,则会被转换成对应数值;若所有字符不都为数字,则转换为
NaN
;空字符串被转换为0
; - 原始值为布尔类型,true会被转换为1,false会被转换为0
- 原始值为
null
会被转换为0 - 原始值为
undefined
会被转换为NaN
- 原始值为字符串类型,若所有字符都为数字,则会被转换成对应数值;若所有字符不都为数字,则转换为
-
在字符串前放一个加号。这与调用Number构造函数效果相同是因为JS引擎在执行它们的时候都会自动调用一个ToNumber的函数,以此来将原始值转换为数值
console.log(+'123'); // 123
3. 原始值转字符串
- 直接调用
String()
,传入原始值作为参数。传入的的所有的原始类型都会被直接在两边加上引号
原始值转对象
new XXX()
想要转成哪种对象就new它对应的包装器。比如数值123转成字符串对象:- 或者直接使用
Object()
构造函数
console.log(new String(123)); // [String: '123']
隐式类型转换
对象转原始值
对象转原始值我们通常只聊隐式的,因为日常开发中隐式转换的情况更常见,对比如 if判断语句中对象转布尔值:
let a = {}
if(a) {
console.log('hello');
}
也可能是计算过程中对象与与原始值相加'1' + [1]
.....不论哪种,大多都不是开发者特地调用指定函数转换的,而是js引擎自动发生的。
转换方式:
- 对象转布尔:任何对象转布尔都是
true
- 对象转字符串:js引擎自动调用
ToString
方法,ToString方法内部又会调用ToPrimitive
方法将对象转成原始类型 - 对象转数字:同理,js引擎自动调用
ToNumber
方法,ToNumber方法内部又会调用ToPrimitive
方法将对象转成原始类型
ToPrimitive()
ToPrimitive() 用于把对象转成原始类型,这种方法不是给我们开发者调用的,而是发生隐式类型转换时只有js引擎自己能用。它接收两个参数,第一个是要转换的对象,第二个是这个对象通过 ToPrimitive() 最终想转换成的类型。
- 若要转成String类型,ToPrimitive(obj, String)
- 如果
obj
接收到的是原始值,直接返回值 - 如果接收到的是对象,调用
toString
,若得到原始值,直接返回 - 否则,调用
valueOf
,若得到原始值,直接返回 - 如果
valueOf()
返回的不是原始值,报错
- 若要转成Number类型,ToPrimitive(obj, Number)
- 如果
obj
接收到的是原始值,直接返回值 - 如果接收到的是对象,调用
valueOf
,若得到原始值,直接返回 - 否则调用
toString
,若得到原始值,直接返回 - 如果
toString()
返回的不是原始值,报错
ToPrimitive()
转换至 String 类型或 Number 类型的过程区别就是调用toString
方法和调用valueOf
方法的先后顺序
toString()
toString方法有几个版本:
{}.toString()
得到由"[object" 和 class 和 "]" 组成的字符串[].toString()
数组的toString方法重写了对象上的toString方法,返回由数组内部元素以逗号拼接的字符串xx.toString()
返回字符串字面量,比如
let fn = function(){};
console.log( fn.toString() ) // "function () {}"
valueOf()
也可以把对象转成原始值,但是仅限包装类对象
运算符的隐式转换规则
一元运算符 +
前面提到原始值转成数值类型可以在字符串前添加一个加号,效果等同于Number()
,即Number(str) == +str
。同样,一个对象转换成数值也可以用一元运算符 +
- 举例:+[] ===> 0
- js引擎自动调用
ToNumber
方法 - ToPrimitive([], Number)
[].valueOf()
,不能得到原始值[].toString()
,得到空字符串''
,是原始值,ToPrimitive()工作结束
- 空字符串转成0
二元操作符 +
v1 + v2 会发生的过程:
- lprim = ToPrimitive(v1)
- rprim = ToPrimitive(v2)
- 如果 lprim 和 rprim 其中有一个是字符串,那么另一个也要被转换为字符串,两者再拼接
- 如果没有一个是字符串,ToNumber(lprim) + ToNumber(rprim)
举几个二元操作符 +
发生隐式类型转换过程的例子:
- 1 + '1' ===> '11'
- lprim = ToPrimitive(1) = 1,rprim = ToPrimitive('1') = '1'
- 1 + '1'
- ToString(1) + '1'
- '1' + '1' ===> '11'
- null + 1
- lprim = ToPrimitive(null) = 0 , rprim = ToPrimitive(1) = 1
- 0 + 1 ===> 1
- [] + {}
- lprim = ToPrimitive([]) = '' , rprim = ToPrimitive({}) = '[object Object]'
- '' + '[object Object]' ===> '[object Object]'
==
等号两边值相等即为true,不同类型的变量在判断过程中js引擎会进行隐式类型转换
转换过程中需要注意的几点: (参考官方文档[Annotated ES5])(es5.github.io/#x11.9.3)
null == undefined
为 true- 若一方为Number,另一方为String,往Number上转
- 若有一方为布尔,把布尔类型转为 Number
- 若一方为String 或 Number,另一方是对象,就把对象转成原始类型,再根据情况决定双方往 String 上转还是往 Number上转
举例: [] == ![]
- 取反优先级更高,[ ]转换成布尔得到true,再取反得到false
- false转换成数值为 0
- [ ]转换成数值为空字符串
- 空字符串转换成数值为0
- 0 == 0 ===> true
结语
显式类型转换规则还是比较好记的,面试中大多偏向对隐式类型转换过程的考察,通常发生在比较运算符、算术运算符旁,我们需要理解并记忆ToPrimitive被调用的过程以及toString和valueOf方法的使用场景,牢记不同运算符的类型转换规则,每一步的转换都一定有对应的规则摆在那,只要符合规则,就一定能成功解答的。