深入探索JavaScript神鬼莫测的类型转换
前言
JavaScript中的类型转换指的是将一种数据类型转换为另一种数据类型的过程。这主要分为两大类:显式(强制)类型转换和隐式(自动)类型转换。
显示类型转换
- 原始值转布尔(Boolean(xx))
- 原始值转数字(Number(xx))
- 原始值转字符串(String(xx))
- 原始值转对象 (new Xxx())
隐式转换
对象转原始值 -- 通常发生隐式转换
所有对象转原始值都会调用toString()
- 任何对象转为布尔值都是true
- {}.toString() 得到由"[object 和 class 和 ]"组成的字符串
- [].toString() 返回由数组内部元素以逗号拼接的字符串
- xx.toString()
返回字符串字面量
valueOf()
能把包装类转为转为原始值
注:只能把原始值对象转为原始值
ToPrmitive(x)(把对象转为原始类型)
是JavaScript中一个抽象操作,它负责将对象转换为原始数据类型。
ToPrimitive(obj, String) => String(obj)
- 如果接收到的是原始值,直接返回值
- 否则,调用toString方法,如果得到原始值,返回值
- 否则,调用valueOf方法,如果得到原始值,返回值
- 否则,报错
ToPrimitive(obj, Number) => Number(obj)
- 如果接收到的是原始值,直接返回值
- 否则,调用valueOf方法,如果得到原始值,返回值
- 否则,调用toString方法,如果得到原始值,返回值
- 否则,报错
二元操作符
v1 + v2
ToPrimitive走的是转Number类型的步骤
- lprim = ToPrimitive(v1)
- rprim = ToPrimitive(v2)
- 如果 lprim 或者 rprim 是字符串,那么就ToString(lprim)或者ToString(rprim) 再拼接
- 否则,那么就ToNumber(lprim) + ToNumber(rprim)(
ToNumber
是JavaScript中的另一个抽象操作,它的目的是将给定的值转换为一个数字(Number)类型)
==
==
运算符在比较前会执行类型转换
问:1数字 和 '1'字符串相等吗?
问:两个空数组相等吗?
let a = []
let b = []
console.log(a == b);
打印结果
分析
a和b不是同一个数组,js引擎在执行这段代码会先生成一个全局上下文调用栈,又因为对象可以无限大,所以对象会被放入堆当中,a和b会被赋一个值,这个值是引用地址,指向堆中相应地址的值。
显示类型转换
原始值转布尔(Boolean(xx))
在Number类型中 0, NaN
会被转成false,其他会被转成true。
console.log(Boolean(1));
console.log(Boolean(0));
console.log(Boolean(-1));
console.log(Boolean(NaN)); // NaN是number类型
打印结果
在String类型中,空字符会被转为false,其他字符被转为true。
console.log(Boolean('hello'));
console.log(Boolean(''));
打印结果
在Boolean类型中,false会被转为false,true被转为true。
console.log(Boolean(false));
console.log(Boolean(true));
打印结果
Undefined 和 Null都被转为false
console.log(Boolean(undefined));
console.log(Boolean(null));
打印结果
原始值转数字(Number())
字符串转数字
console.log('123')
console.log(Number(''));
console.log(Number(' '));
console.log(Number('hello'));
打印结果
布尔值转数字
console.log(Number(true));
console.log(Number(false));
打印结果
Undefined和Null转为数字
console.log(Number(undefined));
console.log(Number(null));
打印结果
官方文档
原始值转字符串(String())
直接转换为字符串
console.log(String(123));
console.log(String(true));
console.log(String(false));
console.log(String(undefined));
console.log(String(null));
打印结果
原始值转对象(new Xxx())
通过new 一个构造函数得到一个实例对象
对象转原始类型
任何对象转为布尔值都是true
let b = []
if(b) { // Boolean(b)
console.log('hello');
}
打印结果
{}.toString() 得到由"[object 和 class 和 ]"组成的字符串
[].toString() 返回由数组内部元素以逗号拼接的字符串
valueOf()
分析
没转成功,以为a是数组对象。
分析 s是原始值对象,valueOf()只能把原始值对象转为原始值。
ToPrimitive(对象转原始值的原理)
- 把对象转为String类型,会先调用toString()方法,如果toString()方法没有转过来,再调用valueOf()方法。
- 把对象转为Number类型,会先调用valueOf()方法,如果valueOf()方法没有转过来,再调用toString()方法。
示例:
分析
+
是指转为Number类型,对象转为原始类型,说明要借助ToPrimitive,且是转为Number类型,走第二步,调用valueOf() => []
还是一个空数组,接下来再调用toString() => ''
变成了一个空字符串,再把空字符串转为Number类型就是0。
二元操作符
示例一
1 + '1' => '11' 为什么?
分析
先执行ToPrimitive(1) + ToPrimitive('1') => 1 + '1'
,'1'是字符串,再执行toString(1) + toString('1') => '1' + '1' =>'11'
示例二
分析
先执行ToPrimitive([]) + ToPrimitive({}) => '' + '[object Object]'
,都是是字符串,再执行toString('') + toString('[object Object]') => '' + '[object Object]' =>'[object Object]'
示例三
先执行ToPrimitive(null) + ToPrimitive(1) => null + 1
,无字符串,再执行ToNumber(null) + ToNumber(1) => 0 + 1 => 1
关于 ==
js官方文档
示例一
1 == {}的执行结果是什么?
分析
根据官方文档,等号有一边是对象,所以执行1 == ToPrimitive({})
,有个运算符 ==
,所以ToPrimitive会执行Number的步骤,根据上文得知ToPrimitive({}) => {}.valueOf() => {} => {}.toString() => '[object Object]'
,此时再把字符串转为数字类型 '[object Object]' => NaN
于是1 == NaN
被判成false。
示例二
[] == ![]的执行结果是什么?
分析
!
的优先级大于==
所以先执行![]
,又因为!
的执行步骤是先转换为布尔值再取反,所以执行Boolean([]) => true
(对象转为布尔值是true),取反为false,得到[] == false
- 根据官方文档一边是布尔值时,
[] == ToNumber(false) => [] == 0
[]
是一个对象,又有==
,执行ToPrimitive([])
的Number步骤,[] => ''
,所以'' == 0
- 又因为
''
是字符类型,执行ToNumber('') => 0
,0 = 0
返回true。
结语
内容比较多,没看明白需要自己梳理一下哦。
转载自:https://juejin.cn/post/7371011013432229926