JS 型別轉換,小嚐一口

小皮草發表於2018-10-19

JavaScript 型別轉換

字串 數字 布林值
undefined "undefined" NaN false
null "null" 0 false
true "true" 1
false "false" 0
"" 0 false
"1.2" 1.2 true
"one" NaN true
0 "0" false
-0 "0" false
NaN "NaN" false
Infinity "Infinity" true
-Infinity "-Infinity" true
1 "1" true
{} "[object Object]" NaN true
[] "" 0 true
[9] "9" 9 true
[1, 2, 3] "1,2,3" NaN true
['a'] "a" NaN true
function foo(){} "function foo(){}" NaN true

型別轉換的基本規則

  • ToString
  • ToNumber
  • ToBoolean
  • ToPrimitive

ToString

負責非字串字串的強制型別轉換。

基本資料型別
  • null => "null"
  • undefined => "undefined"
  • true => "true"
  • 12 => "12"
物件

除非自行定義,否則 toString() 返回內部屬性 [[Class]] 的值。

這個屬性無法直接訪問,一般通過 Object.prototype.toString(..) 檢視

如果物件有自己的 toString() 方法,字串化時就會呼叫該方法並使用其返回值。

var a = {
    name: 'ym'
}
var b = {
    name: 'ym',
    toString: function() {
        return this.name;
    }
}

a.toString(); // "[object Object]"
b.toString(); // "ym"
複製程式碼
陣列

陣列的預設 toString() 方法經過重新定義

var a = [1, 2, 3];
a.toString(); // "1,2,3"
複製程式碼

ToNumber

基本資料型別
  • true => 1 false => 0
  • undefined => NaN
  • null => 0
物件(包括陣列)

首先會被轉換為相應的基本型別值。如果是非數字的基本型別值,則按上面的規則強制轉換。

將值轉換為相應基本型別的步驟:(ToPrimitive)

  1. 檢查該值是否有 valueOf() 方法。
    • 如果有並且返回基本型別值,就使用該值進行強制型別轉換。
    • 如果沒有就使用 toString() 的返回值進行強制轉換。
  2. 如果 valueOf()toString() 均不返回基本型別值,會產生 TypeError 錯誤。
// case1: 物件只有 valueOf 方法,並且返回基本型別值
// 此時就呼叫 valueOf
var a = {
    valueOf: function() {
        return '42';
    }
}

// case2: 物件只有 toString 方法,並且返回基本型別值
// 此時呼叫 toString
var b = {
    toString: function() {
        return '32';
    }
}

// case3:兩種方法都有。並且 valueOf 返回基本型別值
// 此時呼叫 valueOf
var c = {
    valueOf: function() {
        return '42';
    },
    toString: function() {
        return '32'
    }
}

// case4: 兩種方法都有。但是 valueOf 返回的不是基本型別值,而 toString 返回基本型別值
// 此時呼叫 toString
var d = {
    valueOf: function() {
        return [1, 2];
    },
    toString: function() {
        return '32'
    }
}

// case5: 兩種方法都有。但是返回的都不是基本型別值
// 此時報錯 Uncaught TypeError: Cannot convert object to primitive value
var e = {
    valueOf: function() {
        return [1, 2];
    },
    toString: function() {
        return [1, 3];
    }
}

Number(a); // 42
Number(b); // 32
Number(c); // 42
Number(d); // 32
Number(e); // Uncaught TypeError: Cannot convert object to primitive value
複製程式碼

栗子:chestnut:

// [].valueOf() => []
// [].toString() => ""
// "" => 0
Number([]); // 0

// ['abc'].valueOf() => ['abc']
// ['abc'].toString() => "abc"
// "abc" => NaN
Number(['abc']); // NaN
複製程式碼

ToBoolean

兩類值
  • 可以被強制轉換為 false 的值(注意這一部分的值比較少)

    • undefined
    • null
    • false
    • +0 -0 NaN
    • ""

    注意1[]{} 強制轉換並不是 false 而是 true

    注意2

    var a = new Boolean(false);
    var b = new Number(0);
    var c = new String("");
    
    Boolean(a && b && c) // 三者強制轉換均為 true,因為本質是物件
    複製程式碼
  • 其他(轉換為 true

== 號的比較

兩個值的型別相同

這種情況就只比較值是否相等,不會做型別轉換。

正常情況
1 == 1 // true
'abc' == 'abc' // true
true == true // true
null == null // true
undefined == undefined // true
[] == [] // false
{} == {} //false
複製程式碼
特別情況
NaN == NaN // false
+0 == -0 // true
複製程式碼

兩個值的型別不同

這種情況會發生隱式型別轉換。會將其中之一或者二者都轉換為相同的型別後再進行比較。

數字和字串

不論二者的位置關係是怎麼樣,就是將字串轉換為數字型別

  • a 是數字,b 是字串,返回 a == ToNumber(b)
  • a 是字串,b 是數字,返回 ToNumber(a) == b
// case1: 數字 == 字串
42 == '42' // true
42 === '42' // false

// case2: 字串 == 數字
'42' == 42 // true
複製程式碼

布林和其他型別

  1. 布林型別首先會轉換成數字型別。(同樣跟位置沒有關係)

    => 因此問題變成了數字和其他型別的比較了

// case1:布林 == 字串
// 第一步:true => 1
// 第二步:'42' => 42
// 第三步:1 == 42 => false
true == '42' // false
false == '42' // false

// case2:布林 == 數字
true == 1 // true
false == 0 // true
複製程式碼

null 和 undefined

不論位置,結果為 true。

null == undefined // true
undefined == null //true
複製程式碼

null 和 undefined 與其他型別

均返回 false。

null == false // false
undefined == false // false
null == "" // false
undefined == "" // false
null == 0 // false
undefined == 0 // false
複製程式碼

物件和非物件

物件首先要轉化為基本資料型別

  • a 是物件,b 是非物件,返回 a == ToPrimitive(b)
  • a 是非物件,b 是物件,返回 ToPrimitive(a) == b
// case1
// 第一步:[42] => '42'
// 第二步:'42' => 42
// 第三步:42 == 42 => true
42 == [42] // true

// case2:布林和陣列
// false => 0
// [] => ""
// "" => 0
[] == false // true
// ![] => false
![] == false // true
複製程式碼
'abc' == new String('abc') // true
123 == new Number(123) // true
false == new Boolean(false) // true

null == Object(null) // false
undefined == Object(undefined) // false
NaN == new Number(NaN) // false
複製程式碼

陣列與其他

陣列會轉換為字串,因此問題就變成了字串與其他的比較

[] == false // true
[] == "" // true
[] == 0 // true

// ToBoolean(![]) => false
// [] == false => true
[] == ![] // true

// 因為先進行 !運算,所以 false 不會首先轉換為數字
// 第一步:![] => false
// 第二步:false == false => true
![] == false // true
複製程式碼

注意!:由於 !的優先順序要高於 ==,因此會先進行 !運算,再進入 == 流程

+ 號的隱式轉換

數字和字串運算

兩種情況
  • 字串 + 數字 => 字串
  • 數字 + 字串 => 字串
總結

字串和數字存在加法運算,最終得到字串。

加號和字串

  • + 數字字串 => 數字
  • + 非數字字串 => NaN

參考

  • 《你不知道的JS》

  • 《JavaScript 權威指南》

相關文章