隐式转换规则
1.ToString、ToNumber、ToBoolean、ToPrimitive
首先了解一下 js 数据类型之间转换的基本规则,比如数字
、字符串
、布尔型
、对象
、数组
之间如何进行转换。
1.1 ToString
注意区别
toString
方法,ToString
是指其他类型转换为字符转类型的操作。 这里我们讨论null
、undefined
、布尔型
、普通对象
、数组
转换为字符串的规则
- null:转换为
'null'
- undefined:转换为
'undefined'
- 布尔类型:
true
和false
分别转换为'true'
和'false'
- 数字类型:转换成数字的字符串形式,
11
转换为'11'
,1e21
转换为'1e+21'
- 普通对象:转换字符串相当于直接调用
Object.prototype.toString()
,返回'[object Object]'
- 数组类型:相当于调用
Array.prototype.join()
方法,['1','2','3']
转换成'1,2,3'
,[]
转换为空字符串,数组中的null
、undefined
会被当做空字符串处理
String(null) // 'null'
String(undefined) // 'undefined'
String(11) // '11'
String(1e21) // '1e+21'
String({}) // '[object Object]'
String([]) // ''
String(['', null, undefined]) // ',,'
注意:上面所说的规则是在默认的情况下,修改默认的
toString()
,会影响结果
1.2 ToNumber
ToNumber是指其他类型转换为数字类型的操作
- null:转换为
0
- undefined:转换为
NaN
- 布尔类型:
true
转换为1
,false
转换为0
- 字符串类型:如果是纯数字形式,则转换为对应的数字,空字符串转换为
0
,其他一律按转换失败处理,为NaN
- 对象类型:数组会先被转换为原始类型,也就是
ToPrimitive
,然后再把转换后的原型类型按照上面的规则处理,ToPrimitive
会在下文介绍
Number(null) // 0
Number(undefined) // NaN
Number('123') // 123
Number('') // 0
Number('123e') // NaN
Number([]) // 0
Number(['1', '2']) // NaN
Number({}) // NaN
1.3 ToBoolean
ToBoolean是指其他类型转换为布尔类型的操作
js中的假值只有null
、undefined
、0
、''
、NaN
,其余都为真值
Boolean(null) // false
Boolean(undefined) // false
Boolean(0) // false
Boolean('') // false
Boolean(NaN) // false
Boolean([]) // true
Boolean({}) // true
1.4 ToPrimitive
ToPrimitive是指对象类型(普通对象、数组)转换为原始类型的操作
- 当对象类型需要转换为原始类型时,会先查找对象的
valueOf
方法,如果valueOf
返回的是原始类型的值,那么ToPrimitive
就返回这个结果。 - 如果
valueOf
返回的不是原始类型,或者valueOf
方法不存在,就调用对象的toString
方法,也就是遵循对象的ToString
规则,然后使用toString
的返回值作为ToPrimitive
的返回值。 - 如果
valueOf
和toString
都没有返回原始类型的值,则抛出异常。
Number([]) // 0
Number(['1']) // 1
const obj2 = {
valueOf(){
return '11';
},
toString(){
return '12';
}
}
Number(obj2) // 11
const obj3 = {
toString(){
return '123';
}
}
Number(obj3) // 123
const obj4 = {
toString(){
return {};
}
}
Number(obj4) // Uncaught TypeError: Cannot convert object to primitive value
2.宽松相等(==)比较时的隐式转换规则
宽松相等(==)
和严格相等(===)
的区别在于宽松相等会在比较中进行隐式转换
。现在我们来看看不同情况下的转换规则。
2.1 布尔类型和其他类型比较
- 只要
布尔类型
参与比较,该布尔类型
的值会先被转换为数字类型
- 根据
布尔类型
的ToNumber
规则,true
转换为1
,false
转换为0
false == 0 // true
true == 1 // true
true == 2 // false
2.2 数字类型和字符转类型比较
- 当
数字类型
和字符串类型
进行比较时,字符串类型
会被转换成数字类型
- 根据字符串的
ToNumber
规则,如果是纯数字形式的字符串,则转为对应的数字,空字符转为0
, 否则一律按转换失败处理,转为NaN
0 == '' // true
1 == '1' // true
1e21 == '1e21' // true
Infinity == 'Infinity' // true
true == '1' // true
false == '0' // true
false == '' // true
2.3 对象类型和原始类型比较
- 当
对象类型
和原始类型
做相等比较时,对象类型
会依照ToPrimitive
规则转换为原始类型
'[object Object]' == {} // true
'1,2,3' == [1, 2, 3] // true
[2] == 2 // true
[null] == 0 // true
[undefined] == 0 // true
[] == 0 // true
2.4 null、undefined和其他类型比较
null
和undefined
宽松相等的结果为true,其他都为false
null == undefined // true
null == false // false
undefined == false // false
3.经典面试题
定义一个变量
a
,使得下面的表达式结果为true
a == 1 && a == 2 && a == 3
对象每次和原始类型做
==
比较时,都会进行一次ToPrimitive
操作,那我们是不是可以定义一个包含valueOf
或toString
方法的对象,然后通过某个值的累加来实现?
const a = {
value: 1,
valueOf(){
return this.value++;
}
}
a == 1 && a == 2 && a == 3 // true