JavaScript 类型转换
读完《你不知道的JavaScript(中卷)》中关于类型转换的内容,对于类型转换有了非常清晰的认识。
类型
JavaScript 中有 7 种类型:
null、undefined、 boolean、number、string、symbol、object
除了 object 之外, 其他统称为“基本类型”。
对象转换为基本类型值
分为两种情况来处理。
1. 对象要被转换为字符串
转换步骤如下图所示。
举例如下
// 模拟 toString 返回的不是基本类型值
var obj = {
toString: function() {
return {}
}
}
String(obj) // Uncaught TypeError: Cannot convert object to primitive value
执行后,控制台报错。
// 模拟 toString 返回的不是基本类型值,valueOf 返回的基本类型值
var obj = {
toString: function() {
return {}
},
valueOf:function(){
return null
}
}
String(obj) // "null"
// 模拟 toString 返回的不是基本类型值,valueOf 返回的不是基本类型值
var obj = {
toString: function() {
return {}
},
valueOf:function(){
return {}
}
}
String(obj) // Uncaught TypeError: Cannot convert object to primitive value
2. 对象要被转换为数字
和转换为字符串流程差不多,区别在于转换为数字先判断 valueOf 方法,再判断 toString 方法。
举例如下
var obj = {
valueOf:function(){
return null
},
toString:function(){
return 1
}
}
Number(obj) // 0 valueOf 返回的 null 转换为数字为 0
// valueOf 和 toString 返回的都不是基本类型值
var obj = {
valueOf:function(){
return {}
},
toString:function(){
return {}
}
}
Number(obj) // Uncaught TypeError: Cannot convert object to primitive value
!> Object.create(null) 创建的对象没有 valueOf 和 toString 方法,因此转换时会报错。
显式强制类型转换
转换为字符串
如果对象有自定义 toString 方法,则返回自定义 toString 方法的结果,但是如果 toString 返回的不是基本类型值,转换时会报 TypeError。
var obj = {
toString:function(){
return {}
}
}
String(obj) // Uncaught TypeError: Cannot convert object to primitive value
obj + "" // Uncaught TypeError: Cannot convert object to primitive value
obj.toString() // {}
数组的 toString 方法经过了重新定义。
String( [] ) // ''
String( [null] ) // ''
String( [null, null] ) // ','
String( [undefined] ) // ''
String( [undefined, undefined] ) // ','
String( [{}] ) // '[object Object]'
String( [{toString:function(){return 1}}] ) // '1'
转换为布尔类型
假值有限,因此只要记住所有的假值情况,那么其它的就是真值。
null | undefined | false | +0 | -0 | NaN | "" |
---|---|---|---|---|---|---|
转换为数字类型
Number('') // 0
Number(null) // 0
Number(undefined) // NaN
Number(true) // 1
Number(false) // 0
对象会首先被转换为相应的基本类型值,再进行转换。
Number([]) // 0
// [] valueOf 返回的是 [],因此继续调用 toString 得到基本类型值 "",转换为数字为 0
隐式强制类型转换
转换为字符串
如果 + 的其中一个操作数是字符串,那么执行字符串拼接操作。因此常用x + ""
将 x 转换为字符串。
对象和字符串拼接时,对象转为基本类型值按转为数字进行转换,也就是按上面的“对象要被转换为数字”流程进行转换,即先判断 valueOf,再判断 toString,举例如下。
var obj = {
valueOf: function() {
return 1
},
toString: function() {
return 2
}
}
obj + '' // '1'
转换为布尔值
下面的情况会发生布尔值隐式强制类型转换。
- if (..)语句中的条件判断表达式。
- for ( .. ; .. ; .. )语句中的条件判断表达式(第二个)。
- while (..)和do..while(..)循环中的条件判断表达式。
- ? :中的条件判断表达式。
- 逻辑运算符 ||(逻辑或)和 &&(逻辑与)左边的操作数(作为条件判断表达式)。
转换为数字类型
常见的转换方法。
+ '2' // 2
'2' - 0 // 2
'2' / 1 // 2
'2' * 1 // 2
+ 'x' // NaN
'x' - 0 // NaN
'x' / 1 // NaN
'x' * 1 // NaN
1 + '2' // '12'
1 + + '2' // 3 即:1 + (+ '2')
== 和 ===
对于两者区别最正确的解释:== 允许在相等比较中进行强制类型转换,而 === 不允许。
== 比较
记住以下 5 条规则,任何相等比较都不怕。
1、字符串和数字之间的相等比较
字符串先转换为数字,然后再比较。
2、其他类型和布尔类型之间的相等比较
布尔类型转换为数字,再进行比较。
3、对象和非对象之间的相等比较
对象转换成基本类型值,按转换为数字的流程转换后进行比较。对象转换优先级最高。
4、null 和 undefined
null == undefined
, 其他类型和 null 均不相等,undefined 也是如此。
5、特殊情况
NaN == NaN // false
-0 == +0 // true
两个对象比较,判断的是两个对象是否指向同一个值。
举例
下面是一些看起来“不可思议”的相等比较,是否可以按上面的规则解释清楚?
"0" == false // true
// 第2条规则,false 转换为数字,结果为 0,等式变为 "0" == 0
// 两边类型不一致,继续转换,第1条规则,"0" 转换为数字,结果为 0,等式变为 0 == 0
false == [] // true
// 第3条规则,[] 转换基本类型值,[].toString(),结果为 "",等式变为 "" == false
// 两边类型不一致,继续转换,第2条规则,false 转换为数字,结果为 0,等式变为 "" == 0
// 两边类型不一致,继续转换,第1条规则,"" 转换为数字,结果为 0,等式变为 0 == 0
0 == [] // true
// 第3条规则,[] 转换基本类型值,[].toString(),结果为 "",等式变为 0 == ""
// 两边类型不一致,继续转换,第1条规则,"" 转换为数字,结果为 0,等式变为 0 == 0
抽象关系比较
关系比较都会转换为 a < b 进行比较,a > b 会被转换成 b < a,a <= b 会被转换成 !(b= b 转换为 !(a<b)
比较规则
- 比较双方首先转换为基本类型,对象按转换为数字的流程进行转换;
- 若有一方不是字符串,则将其转换为数字再进行比较;
- 若有一方是 NaN,结果总是 false。
举例
null >= 0 // true
null <= 0 // true
null == 0 // flase
按照第二条规则,需要将 null 转换为数字,结果为 0,因此 null >= 0 和 null <= 0 是成立的。但是在相等比较规则中(第 4 条),其他类型和 null 均不相等,因此 null == 0 是不成立的。
var obj = {}
obj >= 0 // false
obj <= 0 // false
obj == 0 // false
obj == "[object Object]" // true
按照第 1 条规则,obj 转换结果为 "[object Object]",接着按第 2 条规则,"[object Object]" 转换为数字,结果为 NaN,按照第 3 条,无论怎么比较结果都是 false。
对象转换总结
由于对象转换有两种情况,什么时候按哪个流程进行转换呢?
显示强制类型转换时,非常容易判断,转为字符串则按字符串的流程,转为数字则按数字的流程。
var obj = {
valueOf:function(){
return 1
},
toString:function(){
return 2
}
}
Number(obj) // 1
String(obj) // '2'
其他情况都按转换为数字流程转换。
var obj = {
valueOf:function(){
return 1
},
toString:function(){
return 2
}
}
+obj // 1
obj - 0 // 1
obj + '' // '1'
obj == 1 // true
obj > 1 // false
obj < 1 // false
但是一般情况下,我们不会去重写 valueOf 和 toString,大部分对象(常用的对象和数组)的 valueOf 返回的仍然是对象,因此对象转换为基本类型值可以直接看 toString 返回的值。
new String(1).valueOf() // '1'
new String(1).toString() // '1'
new Number(1).valueOf() // 1
new String(1).toString() // '1'
new Boolean(1).valueOf() // true
new Boolean(1).toString() // 'true'
new Date().valueOf() // 1532571424467
new Date().toString() // "Thu Jul 26 2018 10:17:19 GMT+0800 (中国标准时间)"
var arr=[1,2,3]
arr.valueOf() === arr // true
arr.toString() // "1,2,3"
var obj = {a:1}
obj.valueOf() === obj // true
obj.toString() // "[object Object]"
var fun = function(){}
fun.valueOf() === fun // true
fun.toString() // "function(){}"