js类型转换

96
oWSQo
2018.11.30 18:14 字数 1512

练习

[] == ![] // true  
[] == 0 // true  
[2] == 2 // true  
['0'] == false // true  
'0' == false // true  
[] == false // true 
[null] == 0 // true
null == 0 // false
[null] == false // true  
null == false // false  
[undefined] == false // true  
undefined == false // false

JS隐式转换规则

ToString

这里所说的ToString可不是对象的toString方法,而是指其他类型的值转换为字符串类型的操作。
这里我们讨论nullundefined、布尔型、数字、数组、普通对象转换为字符串的规则。

  • null:转为"null"
  • undefined:转为"undefined"
  • 布尔类型:truefalse分别被转为"true""false"
  • 数字类型:转为数字的字符串形式,如10转为"10"1e21转为"1e+21"
  • 数组:转为字符串是将所有元素按照","连接起来,相当于调用数组的Array.prototype.join()方法,如[1, 2, 3]转为"1,2,3",空数组[]转为空字符串,数组中的nullundefined,会被当做空字符串处理
  • 普通对象:转为字符串相当于直接使用Object.prototype.toString(),返回"[object Object]"
String(null) // 'null'  
String(undefined) // 'undefined'  
String(true) // 'true'  
String(10) // '10'  
String(1e21) // '1e+21'  
String([1,2,3]) // '1,2,3'  
String([]) // ''  
String([null]) // ''  
String([1, undefined, 3]) // '1,,3'  
String({}) // '[object Objecr]'

对象的toString方法,满足上述操作的规则。

ToNumber

ToNumber指其他类型转换为数字类型的操作。

  • null: 转为0
  • undefined:转为NaN
  • 字符串:如果是纯数字形式,则转为对应的数字,空字符转为0, 否则一律按转换失败处理,转为NaN
  • 布尔型:truefalse被转为1和0
  • 数组:数组首先会被转为原始类型,也就是ToPrimitive,然后在根据转换后的原始类型按照上面的规则处理
  • 对象:同数组的处理
Number(null) // 0  
Number(undefined) // NaN  
Number('10') // 10  
Number('10a') // NaN  
Number('') // 0   
Number(true) // 1  
Number(false) // 0  
Number([]) // 0  
Number(['1']) // 1  
Number({}) // NaN

ToBoolean

ToBoolean指其他类型转换为布尔类型的操作。
js中的假值只有falsenullundefined、空字符、0和NaN,其它值转为布尔型都为true

Boolean(null) // false  
Boolean(undefined) // false  
Boolean('') // flase  
Boolean(NaN) // flase  
Boolean(0) // flase  
Boolean([]) // true  
Boolean({}) // true  
Boolean(Infinity) // true

ToPrimitive

ToPrimitive指对象类型(如:对象、数组)转换为原始类型的操作。
当对象类型需要被转为原始类型时,它会先查找对象的valueOf方法,如果valueOf方法返回原始类型的值,则ToPrimitive的结果就是这个值

如果valueOf不存在或者valueOf方法返回的不是原始类型的值,就会尝试调用对象的toString方法,也就是会遵循对象的ToString规则,然后使用toString的返回值作为ToPrimitive的结果。

如果valueOftoString都没有返回原始类型的值,则会抛出异常。

Number([]) // 0  
Number(['10']) //10  
const obj1 = {
  valueOf () {      
    return 100    
  },
  toString () {      
    return 101    
  }  
}  
Number(obj1) // 100  
const obj2 = {
  toString () {      
    return 102    
  }  
}  
Number(obj2) // 102  
const obj3 = {
  toString () {      
    return {}    
  }  
}  
Number(obj3) // TypeError

前面说过,对象类型在ToNumber时会先ToPrimitive,再根据转换后的原始类型ToNumber

  • Number([]), 空数组会先调用valueOf,但返回的是数组本身,不是原始类型,所以会继续调用toString,得到空字符串,相当于-Number(''),所以转换后的结果为"0"
  • 同理,Number(['10'])相当于Number('10'),得到结果10
  • obj1valueOf方法返回原始类型100,所以ToPrimitive的结果为100
  • obj2没有valueOf,但存在toString,并且返回一个原始类型,所以Number(obj2)结果为102
  • obj3toString方法返回的不是一个原始类型,无法ToPrimitive,所以会抛出错误

宽松相等(==)比较时的隐式转换规则

布尔类型和其他类型的相等比较

只要布尔类型参与比较,该布尔类型的值首先会被转换为数字类型。
根据布尔类型的ToNumber规则,true转为1,false转为0。

false == 0 // true  
true == 1 // true  
true == 2 // false

之前有的人可能觉得数字2是一个真值,所以true == 2应该为真,现在明白了,布尔类型true参与相等比较会先转为数字1,相当于1 == 2,结果当然是false
我们平时在使用if判断时,一般都是这样写

const x = 10  
if (x) {
  console.log(x)  
}

这里if(x)x会在这里被转换为布尔类型,所以代码可以正常执行。但是如果写成这样:

const x = 10  
if (x == true) {
  console.log(x)  
}

代码不会按照预期执行,因为x == true相当于10 == 1

数字类型和字符串类型的相等比较

当数字类型和字符串类型做相等比较时,字符串类型会被转换为数字类型
根据字符串的ToNumber规则,如果是纯数字形式的字符串,则转为对应的数字,空字符转为0, 否则一律按转换失败处理,转为NaN

0 == '' // true  
1 == '1' // true 
1e21 == '1e21' // true  
Infinity == 'Infinity' // true  
true == '1' // true  
false == '0' // true  
false == '' // true

上面比较的结果和你预期的一致吗? 根据规则,字符串转为数字,布尔型也转为数字,所以结果就显而易见了。
这里就不讨论NaN了,因为NaN和任何值都不相等,包括它自己。

对象类型和原始类型的相等比较

当对象类型和原始类型做相等比较时,对象类型会依照ToPrimitive规则转换为原始类型。

'[object Object]' == {} // true
'1,2,3' == [1, 2, 3] // true
[2] == 2 // true
[null] == 0 // true  
[undefined] == 0 // true  
[] == 0 // true

试试valueOf方法

const a = {
  valueOf () {      
    return 10    
}
  toString () {      
    return 20    
  }  
}
a == 10 // true

对象的ToPrimitive操作会先调用valueOf方法,并且avalueOf方法返回一个原始类型的值,所以ToPrimitive的操作结果就是valueOf方法的返回值10。
对象每次和原始类型做==比较时,都会进行一次ToPrimitive操作,那我们是不是可以定义一个包含valueOf方法的对象,然后通过某个值的累加来实现?

const a = {    
  // 定义一个属性来做累加
  i: 1,
  valueOf () {      
    return this.i++    
  }  
}
a == 1 && a == 2 && a == 3 // true

结果正如你所想的,是正确的。当然,当没有定义valueOf方法时,用toString方法也是可以的。

const a = {    
  // 定义一个属性来做累加
  i: 1,
  toString () {      
    return this.i++    
  }  
}
a == 1 && a == 2 && a == 3 // true

null、undefined和其他类型的比较

nullundefined宽松相等的结果为true,这一点大家都知道。其次,nullundefined都是假值,那么:

null == false // false  
undefined == false // false

为什么呢? 首先,false转为0,然后呢? 没有然后了,ECMAScript规范中规定nullundefined之间互相宽松相等(==),并且也与其自身相等,但和其他所有的值都不宽松相等(==)。

JavaScript
Web note ad 1