JS語法: 由++[[]][+[]]+[+[]] = 10 ?引發的問題

解釋:爲什麼 ++[[]][+[]]+[+[]] = 10

原文:爲什麼 ++[[]][+[]]+[+[]] = 10

[0]是一個帶有0成員的數組,[0][0]是取它的第1個成員,所以必是0

用了[0][0] = '1'雖然改了第1成員的值,但下一個[0][0]是獨立的取成員值的表達式,所以得到0數字值。

[] = 1是右值不是iterable(可迭代的)造成的錯誤,這應該是”解構賦值”造成的錯誤,以不同的瀏覽器調試:

數組解構賦值的話,右值必需是iterable(可迭代的),下面的例子的錯誤與[] = 1是一樣錯誤,所以應該會先檢查右值是否爲iterable時,先拋出類型錯誤:

最後的,[] = '1'不會有錯誤,是因爲字符串是屬於iterable(可迭代的)。

解構賦值可以參考這篇文章

JS的{} + {}與{} + []的結果是什麼?

ToPrimitive內部運算

因此,加號運算符只能使用於原始數據類型,那麼對於對象類型的值,要如何轉換爲原始數據類型?下面說明是如何轉換爲原始數據類型的。

在ECMAScript 6th Edition #7.1.1,有一個抽象的ToPrimitive運算,它會用於對象轉換爲原始數據類型,這個運算不只會用在加號運算符,也會用在關係比較或值相等比較的運算中。下面有關於ToPrimitive的說明語法:

ToPrimitive(input, PreferredType?)input代表代入的值,而PreferredType可以是數字(Number)或字符串(String)其中一種,這會代表”優先的”、”首選的”的要進行轉換到哪一種原始類型,轉換的步驟會依這裏的值而有所不同。但如果沒有提供這個值也就是預設情況,則會設置轉換的hint值爲”default”。這個首選的轉換原始類型的指示(hint值),是在作內部轉換時由JS視情況自動加上的,一般情況就是預設值。

而在JS的Object原型的設計中,都一定會有兩個valueOftoString方法,所以這兩個方法在所有對象裏面都會有,不過它們在轉換e有可能會交換被調用的順序。

當PreferredType爲數字(Number)時

當PreferredType爲數字(Number)時,input爲要被轉換的值,以下是轉換這個input值的步驟:

  1. 如果input是原始數據類型,則直接返回input。
  2. 否則,如果input是個對象時,則調用對象的valueOf()方法,如果能得到原始數據類型的值,則返回這個值。
  3. 否則,如果input是個對象時,調用對象的toString()方法,如果能得到原始數據類型的值,則返回這個值。
  4. 否則,拋出TypeError錯誤。

當PreferredType爲字符串(String)時

上面的步驟2與3對調.

PreferredType沒提供時,也就是hint爲”default”時

與PreferredType爲數字(Number)時的步驟相同。

數字其實是預設的首選類型,也就是說在一般情況下,加號運算中的對象要作轉型時,都是先調用valueOf再調用toString。
但這有兩個異常,一個是Date對象,另一是Symbol對象,它們覆蓋了原來的PreferredType行爲,Date對象的預設首選類型是字符串(String)

因此你會看到在一些教程文件上會區分爲兩大類對象,一類是 Date 對象,另一類叫 非Date(non-date) 對象。因爲這兩大類的對象在進行轉換爲原始數據類型時,首選類型恰好相反。

模擬代碼說明

JS對於Object與Array的設計

在JS中所設計的Object純對象類型的valueOf與toString方法,它們的返回如下:

valueOf方法返回值: 對象本身。(所以ToPrimitive最後要返回toString的值了
toString方法返回值: “[object Object]”字符串值,不同的內建對象的返回值是”[object type]”字符串,”type”指的是對象本身的類型識別,例如Math對象是返回”[object Math]”字符串。但有些內建對象因爲覆蓋了這個方法,所以直接調用時不是這種值。(注意: 這個返回字符串的前面的”object”開頭英文是小寫,後面開頭英文是大寫)

一元正號(+),具有讓首選類型(也就是hint)設置爲數字(Number)的功能,所以可以強制讓對象轉爲數字類型,一般的對象會轉爲:

這裏首選類型其實本身就是數字,+讓toString輸出的字符串再強轉了一次。

當然,對象的這兩個方法都可以被覆蓋,你可以用下面的代碼來觀察這兩個方法的運行順序,下面這個都是先調用valueOf的情況:

實例

基本類型間運算

  • 字符串 + 其他原始類型字符串在加號運算有最高的優先運算
    • 數字 + 其他的非字符串的原始數據類型數字爲優先
    • 數字/字符串以外的原始數據類型作加法運算就是轉爲數字再運算

    對象類型間運算

    • 空數組 + 空數組

    兩個數組相加,依然按照valueOf -> toString的順序,但因爲valueOf是數組本身,所以會以toString的返回值纔是原始數據類型,也就是空字符串,所以這個運算相當於兩個空字符串在相加,依照加法運算規則第2步驟,是字符串連接運算(concatenation),兩個空字符串連接最後得出一個空字符串。

    • 空對象 + 空對象

    特別注意: {} + {}在不同的瀏覽器有不同結果
    如果在第一個(前面)的空對象加上圓括號(()),這樣JS就會認爲前面是個對象,就可以得出同樣的結果:

    注: 上面說的行爲這與加號運算的第一個(前面)的對象字面值是不是個空對象無關,就算是裏面有值的對象字面,例如{a:1, b:2},也是同樣的結果

    • Date對象

    要得出Date對象中的valueOf返回值,需要使用一元加號(+),來強制轉換它爲數字類型,例如以下的代碼:

    總結

    解構賦值產生的問題

    上述錯誤。

    {name: 1}[name]相當於{name: 1};[name]。解構賦值成功。

    {}問題

    上述錯誤其實是由於,{[name]:1}中{}是表達式,返回對象;{[name]:1};[name] = ‘1’中{}是語句,語句中不允許”[name]:1“,換而言之語句中允許”{name: 1}”寫法。

    {} + {}

    {} + {}的結果是會因瀏覽器而有不同結果,Chrome(v55)中是object Object字符串連接,但其它的瀏覽器則是認爲相當於+{}運算,得出NaN數字類型。

    {} + []的結果是相當於+[],結果是0數字類型。

    Date對象

    Date對象上面有提及是首選類型爲”字符串”的一種異常的對象,這與其他的對象的行爲不同(一般對象會先調用valueOf再調用toString),在進行加號運算時時,它會優先使用toString來進行轉換,最後必定是字符串連接運算(concatenation)

    toString()

    Object.prototype.toString()纔是用來檢測變量本身的類型,typeof是檢測基本類型,instanceof是檢測是否在原型鏈上。(注意一下Object.prototype.toString與Number.prototype.toString、Array.prototype.toString不同)

    toString方法返回值: “[object Object]”字符串值,不同的內建對象的返回值是”[object type]”字符串,”type”指的是對象本身的類型識別,例如Math對象是返回”[object Math]”字符串。但有些內建對象因爲覆蓋了這個方法,所以直接調用時不是這種值。(注意: 這個返回字符串的前面的”object”開頭英文是小寫,後面開頭英文是大寫。

    Number()、String()與Boolean()

    常被搞混的是直接使用Number()、String()與Boolean()三個強制轉換函數的用法,這與包裝對象的用法不同,包裝對象是必須使用new關鍵字進行對象實例化的,例如new Number(123),而Number(‘123’)則是強制轉換其他類型爲數字類型的函數。

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