JavaScript深刻之頭疼的類型轉換(下)

前言

舉個例子:前端

console.log(1 + '1')

在 JavaScript 中,這是徹底能夠運行的,不過你有沒有好奇,爲何 1 和 '1' 分屬不一樣的數據類型,爲何就能夠進行運算呢?git

這實際上是由於 JavaScript 自動的將數據類型進行了轉換,咱們一般稱爲隱式類型轉換。可是咱們都知道,+運算符既能夠用於數字加法,也能用於字符串拼接,那在這個例子中,是將數字 1 轉成字符串 '1',進行拼接運算?仍是將字符串 '1' 轉成數字 1,進行加法運算呢?github

先賣個關子,雖然估計你也知道答案。今天,咱們就常見的隱式類型轉化的場景進行介紹。面試

一元操做符 +

console.log(+'1');

當 + 運算符做爲一元操做符的時候,查看 ES5規範1.4.6,會調用 ToNumber 處理該值,至關於 Number('1'),最終結果返回數字 1數組

那麼下面的這些結果呢?微信

console.log(+[]);
console.log(+['1']);
console.log(+['1', '2', '3']);
console.log(+{});

既然是調用 ToNumber 方法,回想《JavaScript 深刻之頭疼的類型轉換(上)》中的內容,當輸入的值是對象的時候,先調用 ToPrimitive(input, Number) 方法,執行的步驟是:閉包

  1. 若是 obj 爲基本類型,直接返回
  2. 不然,調用 valueOf 方法,若是返回一個原始值,則 JavaScript 將其返回。
  3. 不然,調用 toString 方法,若是返回一個原始值,則JavaScript 將其返回。
  4. 不然,JavaScript 拋出一個類型錯誤異常。

+[] 爲例,[] 調用 valueOf 方法,返回一個空數組,由於不是原始值,調用 toString 方法,返回 ""app

獲得返回值後,而後再調用 ToNumber 方法,"" 對應的返回值是 0,因此最終返回 0函數

剩下的例子以此類推。結果是:工具

console.log(+['1']); // 1
console.log(+['1', '2', '3']); // NaN
console.log(+{}); // NaN

二元操做符 +

規範

如今 + 運算符又變成了二元操做符,畢竟它也是加減乘除中的加號

1 + '1' 咱們知道答案是 '11',那 null + 1[] + [][] + {}{} + {} 呢?

若是要了解這些運算的結果,不可避免的咱們要從規範下手。

規範地址:http://es5.github.io/#x11.6.1

不過此次就不直接大段大段的引用規範了,直接給你們講簡化後的內容。

到底當執行 + 運算的時候,會執行怎樣的步驟呢?讓咱們根據規範11.6.1 來捋一捋:

當計算 value1 + value2時:

  1. lprim = ToPrimitive(value1)
  2. rprim = ToPrimitive(value2)
  3. 若是 lprim 是字符串或者 rprim 是字符串,那麼返回 ToString(lprim) 和 ToString(rprim)的拼接結果
  4. 返回 ToNumber(lprim) 和 ToNumber(rprim)的運算結果

規範的內容就這樣結束了。沒有什麼新的內容,ToStringToNumberToPrimitive都是在《JavaScript 深刻之頭疼的類型轉換(上)》中講到過的內容,因此咱們直接進分析階段:

讓咱們來舉幾個例子:

1.Null 與數字

console.log(null + 1);

按照規範的步驟進行分析:

  1. lprim = ToPrimitive(null) 由於null是基本類型,直接返回,因此 lprim = null
  2. rprim = ToPrimitive(1) 由於 1 是基本類型,直接返回,因此 rprim = null
  3. lprim 和 rprim 都不是字符串
  4. 返回 ToNumber(null) 和 ToNumber(1) 的運算結果

接下來:

ToNumber(null) 的結果爲0,(回想上篇 Number(null)),ToNumber(1) 的結果爲 1

因此,null + 1 至關於 0 + 1,最終的結果爲數字 1

這個還算簡單,看些稍微複雜的:

2.數組與數組

console.log([] + []);

依然按照規範:

  1. lprim = ToPrimitive([]),[]是數組,至關於ToPrimitive([], Number),先調用valueOf方法,返回對象自己,由於不是原始值,調用toString方法,返回空字符串""
  2. rprim相似。
  3. lprim和rprim都是字符串,執行拼接操做

因此,[] + []至關於 "" + "",最終的結果是空字符串""

看個更復雜的:

3.數組與對象

// 二者結果一致
console.log([] + {});
console.log({} + []);

按照規範:

  1. lprim = ToPrimitive([]),lprim = ""
  2. rprim = ToPrimitive({}),至關於調用 ToPrimitive({}, Number),先調用 valueOf 方法,返回對象自己,由於不是原始值,調用 toString 方法,返回 "[object Object]"
  3. lprim 和 rprim 都是字符串,執行拼接操做

因此,[] + {} 至關於 "" + "[object Object]",最終的結果是 "[object Object]"。

下面的例子,能夠按照示例類推出結果:

console.log(1 + true);
console.log({} + {});
console.log(new Date(2017, 04, 21) + 1) // 這個知道是數字仍是字符串類型就行

結果是:

console.log(1 + true); // 2
console.log({} + {}); // "[object Object][object Object]"
console.log(new Date(2017, 04, 21) + 1) // "Sun May 21 2017 00:00:00 GMT+0800 (CST)1"

注意

以上的運算都是在 console.log 中進行,若是你直接在 Chrome 或者 Firebug 開發工具中的命令行直接輸入,你也許會驚訝的看到一些結果的不一樣,好比:

type1

咱們剛纔才說過 {} + [] 的結果是 "[object Object]" 吶,這怎麼變成了 0 了?

不急,咱們嘗試着加一個括號:

type2

結果又變成了正確的值,這是爲何呢?

其實,在不加括號的時候,{} 被當成了一個獨立的空代碼塊,因此 {} + [] 變成了 +[],結果就變成了 0

一樣的問題還出如今 {} + {} 上,並且火狐和谷歌的結果還不同:

> {} + {}
// 火狐: NaN
// 谷歌: "[object Object][object Object]"

若是 {} 被當成一個獨立的代碼塊,那麼這句話至關於 +{},至關於 Number({}),結果天然是 NaN,但是 Chrome 卻在這裏返回了正確的值。

那爲何這裏就返回了正確的值呢?我也不知道,歡迎解答~

== 相等

規範

"==" 用於比較兩個值是否相等,當要比較的兩個值類型不同的時候,就會發生類型的轉換。

關於使用"=="進行比較的時候,具體步驟能夠查看規範11.9.5

當執行x == y 時:

  1. 若是x與y是同一類型:

    1. x是Undefined,返回true
    2. x是Null,返回true
    3. x是數字:

      1. x是NaN,返回false
      2. y是NaN,返回false
      3. x與y相等,返回true
      4. x是+0,y是-0,返回true
      5. x是-0,y是+0,返回true
      6. 返回false
    4. x是字符串,徹底相等返回true,不然返回false
    5. x是布爾值,x和y都是true或者false,返回true,不然返回false
    6. x和y指向同一個對象,返回true,不然返回false
  2. x是null而且y是undefined,返回true
  3. x是undefined而且y是null,返回true
  4. x是數字,y是字符串,判斷x == ToNumber(y)
  5. x是字符串,y是數字,判斷ToNumber(x) == y
  6. x是布爾值,判斷ToNumber(x) == y
  7. y是布爾值,判斷x ==ToNumber(y)
  8. x不是字符串或者數字,y是對象,判斷x == ToPrimitive(y)
  9. x是對象,y不是字符串或者數字,判斷ToPrimitive(x) == y
  10. 返回false

以爲看規範判斷太複雜?咱們來分幾種狀況來看:

1. null和undefined

console.log(null == undefined);

看規範第二、3步:

  1. x是null而且y是undefined,返回true
  2. x是undefined而且y是null,返回true

因此例子的結果天然爲 true

這時候,咱們能夠回想在《JavaScript專題之類型判斷(上)》中見過的一段 demo,就是編寫判斷對象的類型 type 函數時,若是輸入值是 undefined,就返回字符串 undefined,若是是 null,就返回字符串 null

若是是你,你會怎麼寫呢?

下面是 jQuery 的寫法:

function type(obj) {
    if (obj == null) {
        return obj + '';
    }
    ...
}

2. 字符串與數字

console.log('1' == 1);

結果確定是true,問題在因而字符串轉化成了數字和數字比較仍是數字轉換成了字符串和字符串比較呢?

看規範第四、5步:

4.x是數字,y是字符串,判斷x == ToNumber(y)

5.x是字符串,y是數字,判斷ToNumber(x) == y

結果很明顯,都是轉換成數字後再進行比較

3. 布爾值和其餘類型

console.log(true == '2')

當要判斷的一方出現 false 的時候,每每最容易出錯,好比上面這個例子,憑直覺應該是 true,畢竟 Boolean('2') 的結果但是true,但這道題的結果倒是false。

歸根到底,仍是要看規範,規範第六、7步:

6.x是布爾值,判斷ToNumber(x) == y

7.y是布爾值,判斷x ==ToNumber(y)

當一方出現布爾值的時候,就會對這一方的值進行ToNumber處理,也就是說true會被轉化成1,

true == '2' 就至關於 1 == '2' 就至關於 1 == 2,結果天然是 false

因此當一方是布爾值的時候,會對布爾值進行轉換,由於這種特性,因此儘可能少使用 xx == truexx == false 的寫法。

好比:

// 不建議
if (a == true) {}

// 建議
if (a) {}
// 更好
if (!!a) {}

4. 對象與非對象

console.log( 42 == ['42'])

看規範第八、9步:

  1. x不是字符串或者數字,y是對象,判斷x == ToPrimitive(y)
  2. x是對象,y不是字符串或者數字,判斷ToPrimitive(x) == y

以這個例子爲例,會使用 ToPrimitive 處理 ['42'],調用valueOf,返回對象自己,再調用 toString,返回 '42',因此

42 == ['42'] 至關於 42 == '42' 至關於42 == 42,結果爲 true

到此爲止,咱們已經看完了第二、三、四、五、六、七、八、9步,其餘的一律返回 false。

其餘

再多舉幾個例子進行分析:

console.log(false == undefined)

false == undefined 至關於 0 == undefined 不符合上面的情形,執行最後一步 返回 false

console.log(false == [])

false == [] 至關於 0 == [] 至關於 0 == '' 至關於 0 == 0,結果返回 true

console.log([] == ![])

首先會執行 ![] 操做,轉換成 false,至關於 [] == false 至關於 [] == 0 至關於 '' == 0 至關於 0 == 0,結果返回 true

最後再舉一些會讓人踩坑的例子:

console.log(false == "0")
console.log(false == 0)
console.log(false == "")

console.log("" == 0)
console.log("" == [])

console.log([] == 0)

console.log("" == [null])
console.log(0 == "\n")
console.log([] == 0)

以上均返回 true

其餘

除了這兩種情形以外,其實還有不少情形會發生隱式類型轉換,好比if? :&&等狀況,但相對來講,比較簡單,就再也不講解。

深刻系列

JavaScript 深刻系列目錄地址:https://github.com/mqyqingfen...

JavaScript 深刻系列預計寫十五篇左右,旨在幫你們捋順JavaScript底層知識,重點講解如原型、做用域、執行上下文、變量對象、this、閉包、按值傳遞、call、apply、bind、new、繼承等難點概念。

若是有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。若是喜歡或者有所啓發,歡迎 star,對做者也是一種鼓勵。

再其餘

如今是校招季,淘系前端正在幫助21屆同窗們收割大廠offer,專門建立了交流羣,能夠在這裏提問題,交流面試經驗,這裏也提供了簡歷輔導和答疑解惑等服務,加淘小招微信邀你入羣,微信號 taoxiaozhao233

相關文章
相關標籤/搜索