js中的类型转化包括显示转化,比如:调用Number()
、String()
、Boolean()
,对类型进行强转,这个比较明确简单,但是隐式的类型转化情况就比较多,也非常复杂。所以本篇文章,博主就总结一下js中类型转化的各种情况,主要是针对隐式类型转化,希望能达到覆盖全部情况的效果。
各类型的类型转化结果
js中的类型转化只有三种情况,转为number
、string
、boolean
,就是说无论什么类型(Symbol除外),进行显式或者隐式的类型转化,只能转成这三种类型。
另外Symbol类型比较特殊,下面所有类型转化是基于es5的数据类型,也就是:
string、number、boolean、undefined、null、object
这几种类型进行介绍,Symbol类型不进行介绍。
转为number
原始值为字符串:
'1' => 1
'' => 0
'00' => 0 // 无论多少个0都会转成一个
'a' => NaN // 非空字符串且非数字字符串都会变成NaN
原始值为布尔
true => 1
false => 0
原始值为undefined或null
undefined => NaN
null => 0
原始值为数组(默认情况)
[] => 0
[1] => 1
['1'] => 1
[1,1] => NaN // 除了只有一个元素且元素为数字或数字字符串外,都是NaN
原始值为函数或对象(默认情况)
function(){} {} => NaN
{} => NaN
转为string
原始值为基础类型
1 => '1'
NaN => 'NaN'
true => 'true'
false => 'false'
undefined => 'undefined'
null => 'null'
原始值为引用类型(默认情况)
[] => ''
[20] => '20'
['20'] => '20'
['a'] => 'a'
['a', '10'] => 'a,10' // 数组转为字符串相当于调用join()
function() {} => 'function() {} '
{} => '[object Object]'
{ a:1 } => '[object Object]'
转为boolean
原始值为基础类型
1 => true
0 => false
NaN => false
'' => false
'0' => true // 非空字符串都是true
undefined => false
null => false
原始值为引用类型
全部为 true
引用类型转化规则
注意到我上面在写引用类型进行类型转化的时候,都加上了一个默认情况,那什么情况是特殊的呢,这里说先说三个关键词:Symbol.toPrimitive
、valueOf
、toString
Symbol.toPrimitive、valueOf、toString
演示代码1
下面来看一段代码:
const obj = {
[Symbol.toPrimitive]: function() {
return '超人鸭'
},
valueOf: function() {
return '1'
},
toString: function() {
return '2'
}
}
console.log(Number(obj))
console.log(String(obj))
console.log(obj == '超人鸭')
猜猜打印结果是什么?
答案:
console.log(Number(obj)) // NaN
console.log(String(obj)) // '超人鸭'
console.log(obj == '超人鸭') // true
这个打印结果对应上面三个方法的哪一个的返回结果呢,很明显是Symbol.toPrimitive
演示代码2
再看看下面这段代码:
const obj = {
valueOf: function() {
return '1'
},
toString: function() {
return '2'
}
}
console.log(Number(obj))
console.log(String(obj))
console.log(obj == '1')
现在对象去掉了 Symbol.toPrimitive
方法,猜猜打印结果
答案:
console.log(Number(obj)) // 1
console.log(String(obj)) // '2'
console.log(obj == '1') // true
这就有点不同了,从结果上看Number(obj)
和obj == '1'
对应的是valueOf
方法,String(obj)
对应的是toString
方法
演示代码3
再看看下面这段代码:
const obj = {
valueOf: function() {
return {}
},
toString: function() {
return '2'
}
}
console.log(Number(obj))
console.log(String(obj))
console.log(obj == '2')
valueOf返回值返回一个空对象,那么此时的结果是什么呢?
直接上答案:
console.log(Number(obj)) // 2
console.log(String(obj)) // '2'
console.log(obj == '2') // true
很明显对应的是toString
方法,是不是有点乱了呢
引用类型转化规则总结
通过阅读上面的三个演示代码,相信博主下面的总结你能看得非常清晰
引用类型进行类型转化时会调用本身的Symbol.toPrimitive
、valueOf
、toString
三个方法进行转化,其中Symbol.toPrimitive
的优先级最高,如果要进行转化的引用类型上有这个方法,那么直接调用这个方法,如果没有,再根据转化的情况决定优先调用valueOf
还是toString
。
先说一下Symbol.toPrimitive
这个方法,js对象本身默认是没有这个方法的,且这个方法必须返回基础类型,如果返回引用类型,那么就会报错:
const obj = {
[Symbol.toPrimitive]: function() {
return {}
}
}
如果引用类型上没有这个方法,那么根据转化的情况决定优先调用
valueOf
还是toString
,那么什么是转化的情况呢?可以分为两种:
1. 期待转化为字符串的
2. 转化为其他的
那么在js中,什么情况是期待转化为字符串的呢?
主要是这三种:
1. alert方法
2. 模板字符串
3. 对象的键名
除这三种外(当然还包括显式转化调用String()),都属于其他转化情况,如果期待转化为字符串的,那么优先调用toString
方法,其他情况优先调用valueOf
,注意这里是优先调用,而不是直接调用,这个下面会说,先看看上面说的结果:
const obj = {
toString: function() {
return '我是超人鸭'
},
valueOf: function() {
return '我爱你'
}
}
alert(obj)
console.log(`${obj}`)
const foo = {}
foo[obj] = 'chaorenya'
console.log(foo)
除此之外其他会导致类型转化的操作,都是优先调用
valueOf
方法:
const obj = {
toString: function() {
return '我是超人鸭'
},
valueOf: function() {
return '1'
}
}
console.log(obj == '1')
console.log(+obj)
console.log(obj + 1)
console.log(obj < 2)
回到上面说到
valueOf
、toString
优先的问题,为什么是优先,这里还有一个规则,这两个方法如果一个没有返回基础类型的数据,那么就调用下一个,如果两个方法都没有返回基础类型的数据,就会报错。我把上面优先调用
toString
的代码改造一下:
const obj = {
toString: function() {
return {}
},
valueOf: function() {
return '我爱你'
}
}
alert(obj)
console.log(`${obj}`)
const foo = {}
foo[obj] = 'chaorenya'
console.log(foo)
将toString
方法返回一个引用类型,下面看看结果:
可以看到,调用的就是
valueOf
方法,虽然优先调用toString
,但是toString
没有返回基础类型,所以会调用valueOf
。
下面总结一下引用类型进行类型转化时的规则:
- 如果有
Symbol.toPrimitive
方法,则直接调用这个方法,且这个方法不能返回引用类型,否则报错。 - 根据转化的情况决定优先调用
toString
还是valueOf
,如果期待的是返回字符串,那么优先调用toString
方法,其他情况优先调用valueOf
方法。 - 如果
toString
或valueOf
返回的不是基础类型的数据,那么就调用下一个,如果两个方法都没有返回基础类型数据,则报错。
类型转化结果分析
通过上面的介绍,特别是引用类型转化的规则,那么平时我们遇到的一些类型转化看似奇怪的现象,都可以很好的理解,并且非常清晰。
引用类型默认情况的valueOf和toString
我们平时写代码应该极少去改变一个对象或数组的toString
和valueOf
方法,都是调用Object.prototype
上的方法,所以下面的结果分析都是基于默认情况。
首先我们看看对象和数组调用toString
和valueOf
方法的结果:
const obj = {
a: '1'
}
const arr = [1,2]
console.log(obj.toString()) // '[object Object]'
console.log(obj.valueOf()) // {a: '1'}
console.log(arr.toString()) // '1,2' // 相当于join()
console.log(arr.valueOf()) // [1, 2]
可以看到,默认情况下,valueOf
都是返回引用类型,基于上面引用类型进行类型转化的规则,在默认情况下,引用类型进行转化都是调用了toString
方法
结果分析
加号运算符
两个变量相加的情况,引用类型基于上面的规则,调用的是toString
方法,而基础类型有相加有一个规律,如果是有一个变量是字符串,那么就会转为字符串,其余情况全部转为数字。
有字符串的情况:
console.log('1' + 1) // '11'
console.log('1' + undefined) // '1undefined'
console.log('1' + null) // '1null'
console.log('1' + false) // '1false'
其余情况都是转为数字:
console.log(1 + false) // 1
console.log(1 + undefined) // NaN
console.log(1 + null) // 1
console.log(undefined + false) // NaN
console.log(undefined + null) // NaN
console.log(null + false) // 0
非加号运算符类型转化
一元运算符和其他数学运算符都可以把类型转为number类型:
console.log(+'1') // 1
console.log(+true) // 1
console.log(+undefined) // NaN
console.log(+null) // 0
console.log(true - null) // 1
console.log('1' * true) // 1
双取反号将变量转为布尔值,引用类型固定转为true,不会调用toString
和valueOf
方法:
console.log(!!'1') // true
console.log(!!0) // false
console.log(!!null) // false
console.log(!!undefined) // false
console.log(!![]) // true
console.log(!!{}) // true
不严格相等类型转化
在进行==
比较的时候,有几种特殊情况:
-
null == undefined
返回true - null和undefined除了和自身、对方比较,与其他类型比较全部返回false
- 比较两边如果有NaN,那么返回false,包括
NaN == NaN
- 两个引用类型比较除非引用地址相同,不然返回false
- 其余情况下,引用类型基于上面的规律,默认情况下调用
toString
方法;基础类型转为number再进行比较。
console.log(null == undefined) // true
console.log(null == 0) // false
console.log(undefined == 'undefined') // false
console.log(NaN == NaN) // false
console.log([] == []) // false,地址不同
console.log([] == 0) // true,[]调用toString变为'',''转为number变为0
console.log(['1'] == 1) // true,[]调用toString变为'1','1'转为number变为1
console.log({} == '[object Object]') // true
console.log(1 == '1') // true
console.log(1 == true) // true