JavaScript 类型转换

原创
前端路迹
2018-7-19 11:49
编辑于 2023-7-19 08:58

读完《你不知道的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'

转换为布尔值

下面的情况会发生布尔值隐式强制类型转换。

  1. if (..)语句中的条件判断表达式。
  2. for ( .. ; .. ; .. )语句中的条件判断表达式(第二个)。
  3. while (..)和do..while(..)循环中的条件判断表达式。
  4. ? :中的条件判断表达式。
  5. 逻辑运算符 ||(逻辑或)和 &&(逻辑与)左边的操作数(作为条件判断表达式)。

转换为数字类型

常见的转换方法。

+ '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)

比较规则

  1. 比较双方首先转换为基本类型,对象按转换为数字的流程进行转换;
  2. 若有一方不是字符串,则将其转换为数字再进行比较;
  3. 若有一方是 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(){}"
转载请注明出处。本文地址: https://www.qinshenxue.com/article/javascript-type-conversion.html
Star支持 评论
关注我的公众号