variable - w3c javascript function




為什麼++[[]][+[]]+[+[]]返回字符串“10”? (6)

  1. 一元加上給定的字符串轉換為數字
  2. 給定字符串的遞增運算符轉換並遞增1
  3. [] ==''。 空字符串
  4. +''或+ []評估0。

    ++[[]][+[]]+[+[]] = 10 
    ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 
    1+0 
    10
    

這是有效的,並返回JavaScript中的字符串"10"更多示例 ):

++[[]][+[]]+[+[]]

為什麼? 這裡發生了什麼?


+ []求值為0,然後求和(+操作),任何東西都將數組內容轉換為由逗號連接的元素組成的字符串表示形式。

任何其他喜歡採取數組的索引(具有比+操作更重要的)是序數,並沒有什麼意思。


以下內容來自博客文章,回答了我在發布這個問題時仍然關閉的問題。 鏈接指向ECMAScript 3規範(的HTML副本),它仍然是當今常用Web瀏覽器中JavaScript的基準。

首先,評論:這種表達方式永遠不會出現在任何(理智的)生產環境中,並且只能用於練習讀者如何知道JavaScript的骯髒邊緣。 JavaScript運算符在類型之間隱式轉換的一般原則是有用的,一些常見的轉換也是如此,但本例中的大部分細節不是。

表達式++[[]][+[]]+[+[]]可能最初看起來相當具有強制性和模糊性,但實際上相對容易分解為單獨的表達式。 下面我簡單地添加括號以清楚起見; 我可以向你保證他們沒有任何改變,但是如果你想驗證一下,可以隨時閱讀關於分組操作符的信息 。 所以,表達式可以更清晰地寫為

( ++[[]][+[]] ) + ( [+[]] )

打破這一點,我們可以通過觀察+[]求值為0來簡化。 為了滿足自己為什麼這是真的,檢查一元+運算符,並遵循稍微曲折的軌跡,最後ToPrimitive將空數組轉換為空字符串,然後通過ToNumber最終將其轉換為0 。 我們現在可以用0代替+[]每個實例:

( ++[[]][0] ) + [0]

更簡單了。 至於++[[]][0] ,這是bclary.com/2004/11/07/#a-11.4.4++ )的組合,它定義了一個具有單個元素的數組,它本身是一個空數組( [[]] )和一個屬性訪問器[0] )調用由數組文字定義的數組。

所以,我們可以將[[]][0]簡化為[] ,我們有++[] ,對吧? 事實上,情況並非如此,因為評估++[]會拋出一個錯誤,這可能最初看起來很混亂。 然而,對++性質的一點想法使得這一點變得清晰:它用於增加一個變量(例如++i )或一個對象屬性(例如++obj.count )。 它不僅評估價值,還將價值存儲在某個地方。 在++[]的情況下,它無處可放新值(不管它可能是什麼),因為沒有對要更新的對象屬性或變量的引用。 在規範中,這由內部PutValue操作覆蓋,該操作由前綴增量運算符調用。

那麼, ++[[]][0]做什麼? 那麼,通過與+[]類似的邏輯,內部數組被轉換為0並且該值增加1以給我們最終值1 。 外部數組中的屬性0的值更新為1 ,整個表達式的計算結果為1

這給我們留下了

1 + [0]

...這是加法運算符的簡單使用。 兩個操作數首先ToPrimitive ,如果基元值是一個字符串,則執行字符串連接,否則執行數字加法。 [0]轉換為"0" ,所以使用字符串連接,產生"10"

作為一個最後的拋磚引玉,一些可能不太明顯的東西是覆蓋Array.prototypetoString()valueOf()方法中的任何一個都會改變表達式的結果,因為如果在轉換對象轉換為原始值。 例如,以下

Array.prototype.toString = function() {
  return "foo";
};
++[[]][+[]]+[+[]]

......製作"NaNfoo" 。 為什麼會發生這種情況只能作為讀者的練習...


如果我們分裂它,混亂等於:

++[[]][+[]]
+
[+[]]

在JavaScript中, +[] === 0的確如此。 +將某些東西轉換成數字,在這種情況下,它會降至+""0 (請參見下面的規範細節)。

因此,我們可以簡化它( ++優於+ ):

++[[]][0]
+
[0]

因為[[]][0]意味著:從[[]]獲取第一個元素,這是真的:

  • [[]][0]返回內部數組( [] )。 由於引用,說[[]][0] === [] ,但是讓我們調用內部數組A來避免錯誤的表示法。
  • ++[[]][0] == A + 1 ,因為++表示“按1遞增”。
  • ++[[]][0] === +(A + 1) ; 換句話說,它總是一個數字( +1不一定會返回一個數字,而++總是這樣 - 感謝Tim Down指出了這一點)。

再一次,我們可以將混亂簡化成更清晰的東西。 讓我們用[]代替A

+([] + 1)
+
[0]

在JavaScript中,這也是如此: [] + 1 === "1" ,因為[] == "" (加入一個空數組),所以:

  • +([] + 1) === +("" + 1) ,和
  • +("" + 1) === +("1") ,和
  • +("1") === 1

讓我們更簡化一下:

1
+
[0]

而且,這在JavaScript中是正確的: [0] == "0" ,因為它是用一個元素連接一個數組。 連接將連接由,分隔的元素。 用一個元素,你可以推斷出這個邏輯將導致第一個元素本身。

所以,最後我們得到(數字+字符串=字符串):

1
+
"0"

=== "10" // Yay!

+[]規格細節:

這是一個相當迷宮,但要做+[] ,首先它被轉換為一個字符串,因為這就是+說的:

11.4.6一元+運算符

一元+運算符將其操作數轉換為數字類型。

生產UnaryExpression:+ UnaryExpression的計算方法如下:

  1. 讓expr是評估UnaryExpression的結果。

  2. 返回ToNumber(GetValue(expr))。

ToNumber()表示:

目的

應用以下步驟:

  1. 讓primValue為ToPrimitive(輸入參數,提示字符串)。

  2. 返回ToString(primValue)。

ToPrimitive()說:

目的

返回對象的默認值。 通過調用對象的[[DefaultValue]]內部方法來檢索對象的默認值,並傳遞可選的提示PreferredType。 本規範針對8.12.8中的所有本地ECMAScript對象定義[[DefaultValue]]內部方法的行為。

[[DefaultValue]]說:

8.12.8 [[DefaultValue]](提示)

當使用提示字符串調用O的[[DefaultValue]]內部方法時,將執行以下步驟:

  1. 讓toString是使用參數“toString”調用對象O的[[Get]]內部方法的結果。

  2. 如果IsCallable(toString)為true,那麼,

一個。 讓str是調用toString的[[Call]]內部方法的結果,其中O作為此值和一個空參數列表。

灣 如果str是原始值,則返回str。

數組的.toString表示:

15.4.4.2 Array.prototype.toString()

當調用toString方法時,將執行以下步驟:

  1. 讓數組成為在此值上調用ToObject的結果。

  2. 讓func成為調用參數“join”的[[Get]]數組內部方法的結果。

  3. 如果IsCallable(func)為false,那麼讓func成為標準的內置方法Object.prototype.toString(15.2.4.2)。

  4. 返回調用func提供數組的[[Call]]內部方法的結果作為此值和空參數列表。

所以+[]歸結為+"" ,因為[].join() === ""

再次, +被定義為:

11.4.6一元+運算符

一元+運算符將其操作數轉換為數字類型。

生產UnaryExpression:+ UnaryExpression的計算方法如下:

  1. 讓expr是評估UnaryExpression的結果。

  2. 返回ToNumber(GetValue(expr))。

ToNumber被定義為""為:

StringNumericLiteral ::: [empty]的MV值為0。

所以+"" === 0 ,因此+[] === 0


這個評估的結果相同但略小一些

+!![]+''+(+[])
  • [] - 是一個轉換的數組,當它加上或減去時被轉換為0,所以+ [] = 0
  • ![] - 評估為false,因此!! []評估為true
  • + !! [] - 將true轉換為數值,其值為true,所以在這種情況下為1
  • +'' - 向表達式添加一個空字符串,使數字轉換為字符串
  • + [] - 評估為0

所以評估

+(true) + '' + (0)
1 + '' + 0
"10"

所以現在你明白了,試試這個:

_=$=+[],++_+''+$

++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]

然後我們有一個字符串連接

1+[0].toString() = 10




syntax