Js中的类型转换一般分为 显示类型转换 和 隐式类型转换
显式类型转换: 使用包装函数如 Number String Boolean 等对类型进行转换 如Number('3') = 3
隐式类型转换: 不实用包装函数,动态转换。在运算符两侧类型不同的时候,会先进行隐式转换,再进行运算。 如 '1'+0 = '10' [0 转换成字符串'0' 再做运算] false+10=10 [false转换成数字0 再做运算]
下面具体来看类型之间的相互转换规则!
任意类型转number
- boolean转number类型遵循 false -> 0 true ->1
- string转number类型遵循
- 如果字符串包含的字符都是数字,会转换成对应的number。如果开头都是0 则忽略,如Number("00100") = 100 。如果开头是"." 会补0 如Number(".0012131") = 0.0012131
- 如果包含其他的字符,转换成NaN
- 如果是空字符串"",或者仅有空格的字符串. " " , " " , " " ... ,转换成0
总结一下: 能转成数字的转数字,不能转的一律转成NaN
3. Symbol类型无法转换成number类型,会直接报错
4. 特殊的空值转number: undefined -> NaN. null -> 0 (null转为0也是C语言的传统)
5. 复杂类型转number类型,会先进行[[Primitive]]操作,求其原始值,再将其原始值按照上述规 则转换成简单类型。
[[Primitive]]操作
[[Primitive]]操作是javascript引擎的内置方法,其作用是将复杂类型转换成简单类型的原始值.
具体流程如下:
1. 调用复杂类型的valueOf方法,如果返回的是一个简单类型,则返回该值。结束
2. 如果不是简单类型,则调用复杂类型的toString方法,如果返回简单类型,则返回该值,结束。
3. 如果还不是简单类型,返回undefined 结束
我们举一些例子:
DEMO1
const obj = {}
Number(obj) = NaN
obj 为复杂类型,所以在转换之前,js引擎会先调用[[Primitive]]操作,将其转换为基本类型,具体流程为:
1. 调用{}的valueOf方法,其返回值为{}本身,不是一个简单类型
2. 调用{}的toString方法,返回值为 "[object Object]" 是一个简单类型
此时就转换成了 Number("[object Object]") 而 "[object Object]" 无法转换成数字,所以返回NaN
这里你或许会疑问,为什么obj.toString() 为 “[object Object]” ?
这是因为,obj对象本身不存在toString函数,所以会顺着其原型链查找,找到Object.prototype对象。
Object.prototype对象中的toString默认会返回obj对象中的[[Class]]内置属性,在普通对象中,该属性默认值为"[object Object]"
我们可以通过 Object.prototype.toString.call() 插件这个值以及其子类型,这里要用call改变Object.prototype.toString函数的this指向,否则其会一直指向 Object.prototype
DEMO 2
const arr = []
Number(arr) = 0
这里由于数组arr是个复杂类型,要先调用[[Primitive]]操作将其转换为普通类型
1. arr.valueOf 返回的也是其本身
2. 由于数组是Array构造函数的实例,Array.prototype上实现了专用于数组的toString方法,所以arr.toString()会调用这个方法,将数组元素用","拼接,并且返回一个数组,由于arr为空,返回空字符串“”
3. 由于空字符串是普通类型,所以就变成了 Number("") = 0
DEMO 3
const arr = [11]
Number(arr) = 11
和上面的例子类似,[11]转换为普通类型,调用其toString函数,返回"11" 转换成Number(11) = 11
DEMO 4
const arr = [‘abc’]
Number(arr) = NaN
同理,['abc'] 转换为简单类型为 'abc' Number('abc') = NaN
DEMO 5
const arr = [1,2]
Number(arr) = NaN
arr转换成简单类型为 "1,2" Number("1,2") = NaN
DEMO 6
const arr = [null]
DEMO 7
const arr = [undefined]
DEMO 8
const arr = [null,undefined]
这三个一起看 按照上面的例子,你也许会觉得 这三个答案都是NaN
[null] -> 'null' Number('null') = NaN
[undefined] -> 'undefined' Number('undefined') = NaN
[null,undefined] -> 'null,undefined' Number('null,undefined') =NaN
但是其实并不是这样
Array中的toString函数 在遇到null或者undefined时,会跳过,不会加入到最终的字符串中,所以DEMo6 7 的原始值都是 '' 最终结果为 0 而最后一个虽然undefined和null都会跳过,但是两个空字符串会被","链接,所以其原始值为 "," Number(",") = NaN
DEMO 9
Number(new Date()) = 1706000941594 (当前时间的时间戳)
很好理解了 (new Date()).valoeOf() 为当前时间戳,正好也是该Date对象的原始值
所以又获得了一个新的获取时间戳的办法 +(new Date())
任意类型转boolean
这个只需要记住转换为false的情况,剩下的都为false
1. 字符串中,只有空字符串为false,包含空格的字符串都是true 如' '
2. 数字中 NaN +0 -0 为false 其他都是true
3.复杂类型都是true
4.Symbol类型都是true
5. undefined 和 null 都是false
需要注意,new Boolean(false) 是个复杂对象,即便传入的是false,其构造的对象也是true
请不要写 if(new Boolean(false)){} 这样的代码!
任意类型转String
1. boolean转string false-> "false" true->"true"
2. 数字转string
1. 正常数字直接包裹成字符串
2. NaN -> "NaN"
3. Infinity -> "Infinity" -Infinity -> "-Infinity"
4. 0 +0 -0 -> "0"
3. 复杂类型转字符串,会直接调用其内部的toString方法
4. Symbol转字符串,如果是显式转换,即String(Symbol(111)) 则相当于调用Symbol().toString()函数 转换成Symbol(111) 如果是隐式转换 如 Symbol(111)+'111' 会报错 这个记一下
Symbol转换
Symbol转string number boolean上面总结过了,即:
1. Symbol转string 显式转换可以成功 隐公转换报错
2. Symbol转number 会报错
3. Symbol转boolean 一律都是true
任意类型转Symbol 会先转成字符串,然后作为Symbol的描述,如
function foo(){}
console.log(Symbol(foo)) //Symbol(function foo(){})
任意类型转JSON字符串
使用JSON.stringify可以将任意类型转成JSON字符串,使用JSON.parse() 可以解析还原JSON字符串。
但是有些类型转成JSON是不安全的,在parse时,无法还原,包含以下几种情况:
1. undefined 函数 Symbol等 直接stringify会返回undefined,在对象中会直接忽略,在数组中会直接转成 null 如:
2. null NaN +Infinity -Infinity 都会转换成 "null"
3. 对于一些重写了toJSON的函数,会先调用toJSON方法,将其返回值按照规则转换 如moment
4. RegExp Error 这些方法也包含toJSON,其创建的实例对象的JSON转换后为 “{}”
5. 对象的原型链在stringify后会丢失
6. 循环引用转JSON时会报错
面试中,我经常被问过一个问题
有什么办法能拷贝对象?
Object.assign / {...obj}
还有没有别的办法能深拷贝对象?
JSON.parse(JSON.stringify(obj))
那这种办法有什么问题? 这时候你就知道怎么回答了!
ps: 一般这种需要递归写一个拷贝函数 你如果懒得写 还有loash给你用!