類型轉換在各個語言中都存在,而在 JavaScript 中因爲缺少對其的瞭解而不慎在使用中常常形成bug被人詬病。爲了不某些場景下的意外,甚至推崇直接使用 Strict Equality( === )來代替 ==。這確實能避免不少bug,但更是一種對語言不理解的逃避(我的觀點)。數組
先拋出在 You Don’t Know JavaScript (中) 看到的一個例子安全
[] == [] // false [] == ![] // true {} == !{} // false {} == {} // false
是否是很奇怪?本文將從書中看到的知識與規範相結合,來詳細說明一下JavaScript在類型轉換時候發生的故事。函數
不少人喜歡說顯示類型轉換與隱式類型轉換,但我的感受只是說法上的不一樣,實質都在發生了類型轉換而已,故不想去區分他們了(感受一萬我的有一萬種說法)spa
僅在6大基本類型 null undefined number boolean string object 做討論 symbol未考慮code
var a = String(1) var b = Number('1') var c = 1 + '' var d = +'1'
a,b直接調用了原生函數,發生了類型轉換。c,d使用了+運算符的一些規則,發生了類型轉換。這些是很簡單的也是咱們經常使用的。對象
其實真正起做用的,是語言內部對規範中抽象操做的實現,接下來咱們所說的 ToString, ToNumber, ToBoolean
等都是抽象操做,而不是JS裏對應的內置函數ip
按照如下規則轉化被傳遞的參數字符串
Argument Type | Result |
---|---|
Undefined | 「undefined」 |
Null | 「null」 |
Boolean | true -> 「true」 false – > 「false」 |
Number | NaN -> 「NaN」 +0 -0 -> 「0」 -1 -> 「-1」 infinity -> 「Infinity」 較大的數科學計數法 (詳見規範9.8.1) |
String | 不轉換 直接返回 |
Object | 1. 調用ToPrimitive抽象操做, hint 爲 String 將返回值做爲 value 2. 返回ToString(value) |
String(undefined) // "undefined" String(null) // "null" String(true) // "true"
ToPrimitive 抽象操做下面會說起string
按照如下規則轉換被傳遞參數it
Argument Type | Result |
---|---|
Undefined | NaN |
Null | +0 |
Boolean | true -> 1 false -> +0 |
Number | 直接返回 |
String | 若是不是一個字符串型數字,則返回NaN(具體規則見規範9.3.1) |
Object | 1. 調用ToPrimitive抽象操做, hint 爲 Number 將返回值做爲 value 2. 返回ToNumber(value) |
按照如下規則轉換被傳遞參數
Argument Type | Result |
---|---|
Undefined | false |
Null | false |
Boolean | 直接返回 |
Number | +0 -0 NaN -> false 其餘爲true |
String | 空字符串(length爲0) -> false 其餘爲true |
Object | true |
顧名思義,該抽象操做定義了該如何將值轉爲基礎類型(非對象),接受2個參數,第一個必填的要轉換的值,第二個爲可選的hint,暗示被轉換的類型。
按照如下規則轉換被傳遞參數
Argument Type | Result |
---|---|
Undefined | 直接返回 |
Null | 直接返回 |
Boolean | 直接返回 |
Number | 直接返回 |
String | 直接返回 |
Object | 返回一個對象的默認值。一個對象的默認值是經過調用該對象的內部方法[[DefaultValue]]來獲取的,同時傳遞可選參數hint。 |
var a = {} a.toString = function () {return 1} a.valueOf = function () {return 2} String(a) // "1" Number(a) // 2 a + '' // "2" ??????? +a // 2 a.toString = null String(a) // "2" a.valueOf = null String(a) // Uncaught TypeError: balabala
彷佛咱們發現了一個很不合規範的返回值,爲何 a + ''
不該該返回」1″嗎
基礎概念已經瞭解了,那麼在 == 中到底發生了什麼樣的類型轉換,而致使了常常產生出乎意料的bug,致使了它臭名昭著。
x == y 判斷規則以下:
[] == [] // false // 1. 兩遍類型都爲 Object,比較引用地址,不一樣返回false 搞定 [] == ![] // true // 1. ![]強制類型轉換 變爲 [] == false // 2. 根據規範第7條,返回 [] == ToNumber(false), 即 [] == 0 // 3. 根據規範第9條,返回ToPromitive([]) == 0,數組的valueOf爲自己,不是原始值,則返回toString()即 "" == 0 // 4. 根據規範第5條,返回ToNumber("") == 0, 即 0 == 0 // 5. 根據規範第1條,返回 true // 下面的不贅述了,分析相似上面 {} == !{} // false {} == {} // false
咱們不難看出如下幾點
引用 << 你不知道的JS(中) >> 中的2句話
- 若是兩遍的值中有 true 或者 false , 千萬不要使用 == (會被轉爲數字0,1來進行判斷,會出現一些意外的狀況)
- 若是兩遍的值中有[]、」」或者0,儘可能不要使用 ==
先來看看這個例子
var a = { b: 42 } var b = { b: 43 } a < b // false a == b // false a > b // false a <= b // true a >= b // true
是否是感受到世界又崩塌了???
讓咱們來仔細分析一下
var a = { b: 42 } var b = { b: 43 } a < b // false // 1. 兩遍調用ToPrimitive, 返回[object Object] 兩遍一致 返回 false a == b // false // 兩遍不一樣的引用,返回false a > b // false // 同 a < b a <= b // true // 按規範實際上是處理成 !(a > b) 因此爲true a >= b // true
因此在不相等比較的時候,咱們最後仍是進行手動的類型轉換較爲安全
深刻了解類型轉換的規則,咱們就能夠很容易取其精華去其糟粕,寫出更安全也更簡潔可讀的代碼