JavaScript關於類型轉換

一個關於JavaScript類型轉換的問題

( [ ][ [] ] + [ ] )[ -~{} ] + ( {} + {} )[ -~{} - ~{} ]

這篇文章主要通過一條完全由符號組成的語句來分析JavaScript是如何執行的。

首先,將式子分爲兩部分:

 1. ( [][ [] ] + [])[ -~{}] 
 2. ( {} + {} )[ -~{} - ~{} ]

第一部分

先來看第一部分。

  這一部分從總體看是一個通過下標訪問特定的位置的表達式,在這裏簡寫爲

( X ) [ index ]

其中

  • X: [ ][ [] ]+ [ ]
  • index:-~{}

先看X:

X在這裏分爲兩部分,聲明兩個標識符lvalrval,令

  • lval => [ ][ [] ]
  • rval => [ ]

分析AST樹可以看出,這兩部分都是表達式,並且通過二元操作符"+"連接。
查閱ECMAScript規範關於The Addition Operator一節的內容。

  1. lval+rval,實際將轉換爲ToPrimitve(lval)+ToPrimitve(rval)

  2. 對於lval,它是一個通過下標訪問數組元素的表達式,其中數組爲空數組,下標爲“[]”。根據規範Integer Indexed Exotic Objects一節內容,數組的索引必須是整數值。

  3. 因此要將[]轉換爲數值類型。首先調用規範方法ToNumber,對[]進行轉換。即ToNumber([])。
    在這裏插入圖片描述

  4. 查閱規範,因爲[]不是基本類型,所以先調用ToPrimitive,轉換爲基本類型後再調用ToNumber
    在這裏插入圖片描述

  5. 查閱ToPrimitvie,對於數組類型,會先調用數組的valueOf方法,如果不能返回基本類型,再接着調用toString方法。如果都失敗,會拋出TypeError錯誤。

  6. 在這裏插入圖片描述
      對於數組,因爲調用valueOf返回原數組本身,所以接着調用toString方法。

      查閱 Array.prototype.toString ( ),該方法對調用Array.prototype.join ( separator )方法,將數組拼接成字符串,在這裏傳入的拼接符separator 參數爲空,取默認值爲","。
    在這裏插入圖片描述
       根據規範關於Array.prototype.join ( separator )的內容,“[]”是一個空數組,該方法的結果返回空字符串"",長度爲0。

       因此ToPrimitve([])返回空字符串""

  7. 將 [] 轉換爲基本類型後,接着需要對ToPrimitve([])的返回值調用ToNumber,根據規範ToNumber("")返回 0。
       所以lval轉換成 [ ][0],對於數組訪問規則,因爲[]是一個空數組,所以對任何位置的訪問都返回undefined。

  8. 所以ToPrimitive(lval)的值爲undefined。

  9. 接着看rval部分。通過上面對lval部分的求值可知,ToPrimitive([])返回值是""。

  10. 所以lval+rval,轉換爲undefined+"",根據二元操作符加法規則,當任何一方是sring類型時,操作符兩邊都將轉換爲string類型,所以undefined轉換爲string爲“undefined”

lval+rval=“undefined”+""=“undefined”
即X部分結果爲"undefined"

index部分:-~{}

index部分分爲三個小塊,

  1. 一元操作符: -
  2. 一元操作符: ~
  3. 表達式: {}

根據優先級,先求值~{},令其結果爲rst1,再求值 -rst1

求值過程如下:

  1. 根據規範Bitwise NOT Operator ( ~ )一節內容,求值~{},實際會通過ToInt32 ( argument )方法將傳入值轉換爲數值.在這裏插入圖片描述
  2. ToInt32 內部,首先要調用ToNumber將{}轉換爲Number類型。
  3. 根據上面提到過的流程,過程爲ToPrimitve({}),再將其結果調用ToNumber
  4. 根據規範,ToPrimitive({}),實際會最終調用toString方法。根據Object.prototype.toString ( )一節內容,toString返回字符串"[object Object]",所以ToPrimitive({})值爲字符串"[object Object]"
  5. 所以接着調用ToNumber("[object Object]"),根據規範,返回值爲NaN。
  6. 根據ToInt32 方法描述,當ToNumber返回值爲 NaN, +0, ‑0, +∞, 或者 ‑∞時, 一律返回+0.在這裏插入圖片描述
  7. 在這裏,~{} 實際已經轉換爲~0。而~0結果爲-1.所以~{}結果爲-1
  8. 接着計算-(-1),返回1。
  9. 所以index部分的值爲1

因此第一部分(X)[index]轉換爲(“undefined”)[1],是通過索引訪問字符串的操作,返回單個字符"n"。

第二部分

下面再看第二部分

({} + {})[-~{} - ~{}]

這部分從總體看依舊是通過下標訪問一個特定的位置,
在這裏簡寫爲

(Z)[index2]

其中

  • X:{} + {}
  • index:-~{} - ~{}

Z部分:

在這裏需要注意,當{}作爲語句的開頭,它會優先被解釋爲語句塊,而非對象表達式。
所以,我們可以看到{}+[]結果爲0,正是因爲如此。

  但在這裏,因爲Z位於括號裏面,所以這裏{}會被解釋爲表達式

  1. 首先根據二元操作符"+",操作符兩側的表達式會被調用ToPrimitive方法轉換爲基本值。
    即,ToPrimitve({})+ToPrimitve({})

  2. 根據上面已經得出的結論,ToPrimitve({})+ToPrimitve({})會被轉換爲:

    “[object Object]”+"[object Object]"

    所以{} + {} => “[object Object][object Object]”

  3. 因此Z部分值爲"[object Object][object Object]"

index2部分:

  1. 根據上面已經得出的結論,~{}被轉換爲-1,所以-~{} - ~{}轉換爲-(-1)-(-1),值爲2.

  2. 所以第二部分變爲["[object Object][object Object]"][2],依舊是通過數字索引訪問字符串特定字符,返回結果爲"b"。

結論

結合第一部分和第一部分,上式變爲"n"+“b”=>“nb”

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章