JavaScript 类型转换

922 阅读5分钟
原文链接: www.qinshenxue.com

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

// [] 转换基本类型值(toString)为 ""

隐式强制类型转换

转换为字符串

如果 + 的其中一个操作数是字符串,那么执行字符串拼接操作。因此常用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<a),a >= 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

按照第 1 条规则,obj 转换结果为 "[object Object]",接着按第 2 条规则,"[object Object]" 转换为数字,结果为 NaN,按照第 3 条,无论怎么比较结果都是 false。