簡體   English   中英

為什么 ++[[]][+[]]+[+[]] 返回字符串“10”?

[英]Why does ++[[]][+[]]+[+[]] return the string "10"?

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

 console.log(++[[]][+[]]+[+[]])

為什么? 這里發生了什么?

如果我們把它分開,混亂就等於:

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

在 JavaScript 中, +[] === 0是正確的。 +將某些內容轉換為數字,在這種情況下,它將歸結為+""0 (請參閱下面的規范詳細信息)。

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

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

因為[[]][0]意思是:從[[]]獲取第一個元素,所以:

[[]][0]返回內部數組 ( [] )。 由於引用,說[[]][0] === [] ,但讓我們調用內部數組A以避免錯誤的表示法。

++在其操作數之前表示“加一並返回加后的結果”。 所以++[[]][0]等價於Number(A) + 1 (或+A + 1 )。

同樣,我們可以將混亂簡化為更清晰的內容。 讓我們用[]代替A

(+[] + 1)
+
[0]

+[]可以將數組強制轉換為數字0 ,需要先將其強制轉換為字符串,也就是"" 最后,添加1 ,結果為1

  • (+[] + 1) === (+"" + 1)
  • (+"" + 1) === (0 + 1)
  • (0 + 1) === 1

讓我們進一步簡化它:

1
+
[0]

此外,這在 JavaScript 中也是正確的: [0] == "0" ,因為它連接了一個具有一個元素的數組。 加入將連接由,分隔的元素。 對於一個元素,您可以推斷出此邏輯將生成第一個元素本身。

在這種情況下, +看到兩個操作數:一個數字和一個數組。 它現在試圖將兩者強制為同一類型。 首先,數組被強制轉換為字符串"0" ,接下來,數字被強制轉換為字符串 ( "1" )。 數字+字符串===字符串

"1" + "0" === "10" // Yay!

+[]規格詳情:

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

11.4.6 一元+運算符

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

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

  1. 讓 expr 是計算 UnaryExpression 的結果。

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

ToNumber()說:

目的

應用以下步驟:

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

  2. 返回 ToString(primValue)。

ToPrimitive()說:

目的

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

[[DefaultValue]]說:

8.12.8 [[默認值]](提示)

當使用hint String調用O的[[DefaultValue]]內部方法時,采取如下步驟:

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

  2. 如果 IsCallable(toString) 為真,則

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

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

數組的.toString表示:

15.4.4.2 Array.prototype.toString ( )

當調用 toString 方法時,采取以下步驟:

  1. 讓 array 是對 this 值調用 ToObject 的結果。

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

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

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

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

同樣, +定義為:

11.4.6 一元+運算符

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

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

  1. 讓 expr 是計算 UnaryExpression 的結果。

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

ToNumber""定義為:

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

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

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

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

1+[0].toString() = 10

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

首先,評論:這種表達式永遠不會出現在任何(正常的)生產環境中,並且只能作為練習讀者對 JavaScript 的臟邊緣的了解程度。 JavaScript 運算符在類型之間隱式轉換的一般原則很有用,一些常見的轉換也是如此,但在這種情況下的大部分細節都不是。

表達式++[[]][+[]]+[+[]]最初可能看起來相當令人印象深刻和晦澀,但實際上相對容易分解為單獨的表達式。 為了清楚起見,我在下面簡單地添加了括號; 我可以向你保證他們不會改變任何東西,但如果你想驗證,那么請隨意閱讀分組操作符 所以,表達式可以更清楚地寫成

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

分解這一點,我們可以通過觀察+[]評估為0來簡化。 為了滿足自己為什么這是真的,請查看一元 + 運算符並遵循稍微曲折的路徑,最終ToPrimitive將空數組轉換為空字符串,然后由ToNumber最終將其轉換為0 我們現在可以用0代替+[]每個實例:

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

已經更簡單了。 至於++[[]][0] ,這是前綴增量運算符++ )的組合,一個數組字面量定義了一個具有單個元素的數組,該數組本身就是一個空數組( [[]] )和一個屬性訪問器( [0] ) 在由數組字面量定義的數組上調用。

所以,我們可以將[[]][0]簡化為[]並且我們有++[] ,對嗎? 事實上,情況並非如此,因為評估++[]會引發錯誤,這最初可能會令人困惑。 然而,對++的本質稍加思考就清楚了:它用於增加變量(例如++i )或對象屬性(例如++obj.count )。 它不僅計算出一個值,它還將該值存儲在某處。 ++[]的情況下,它無處放置新值(無論它是什么),因為沒有對要更新的對象屬性或變量的引用。 在規范方面,這由內部PutValue操作涵蓋,該操作由前綴增量運算符調用。

那么, ++[[]][0]什么作用呢? 好吧,通過與+[]類似的邏輯,內部數組被轉換為0並且這個值增加1給我們一個最終值1 外部數組中屬性0的值更新為1 ,整個表達式的計算結果為1

這給我們留下了

1 + [0]

...這是加法運算符的簡單用法。 兩個操作數首先被轉換為原始值,如果其中一個原始值是字符串,則執行字符串連接,否則執行數字加法。 [0]轉換為"0" ,因此使用字符串連接,產生"10"

最后一點,可能不是很明顯的事情是覆蓋Array.prototypetoString()valueOf()方法之一將改變表達式的結果,因為在轉換一個對象轉換為原始值。 例如,以下

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

... 產生"NaNfoo" 為什么會發生這種情況留給讀者作為練習......

讓我們簡單點:

++[[]][+[]]+[+[]] = "10"

var a = [[]][+[]];
var b = [+[]];

// so a == [] and b == [0]

++a;

// then a == 1 and b is still that array [0]
// when you sum the var a and an array, it will sum b as a string just like that:

1 + "0" = "10"

這個評估結果相同但小一點

+!![]+''+(+[])
  • [] - 是一個數組被轉換,當你添加或減去它時轉換為 0,因此 +[] = 0
  • ![] - 評估為 false,因此 !![] 評估為 true
  • +!![] - 將 true 轉換為計算結果為 true 的數值,因此在這種情況下為 1
  • +'' - 將一個空字符串附加到表達式中,導致數字被轉換為字符串
  • +[] - 計算結果為 0

所以評估為

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

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

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

+[] 計算結果為 0 [...] 然后將它與任何東西相加(+ 運算)將數組內容轉換為其字符串表示形式,該字符串表示由用逗號連接的元素組成。

任何其他像獲取數組索引(具有比 + 操作更高的優先級)的東西都是有序的,沒什么有趣的。

也許將表達式計算為沒有數字的"10"的最短方法是:

+!+[] + [+[]] // "10"
-~[] + [+[]]  // "10"

解釋

  • +!+[] :
    • +[]被評估為0
    • !0被評估為true
    • +true被評估為1
  • -~[]-(-1) ,被評估為1
  • [+[]] :
    • +[]被評估為 0
    • [0]是具有單個元素0的數組。

然后,JS 計算1 + [0] ,一個Number + Array表達式。 然后 ECMA 規范起作用: +運算符通過調用ToPrimitiveToString抽象操作將兩個操作數轉換為字符串。 如果表達式的兩個操作數都只是數字,則它作為加法函數運行。 訣竅是數組很容易將它們的元素強制轉換為連接的字符串表示形式。

一些例子:

1 + {}            // "1[object Object]"
1 + []            // "1"
1 + new Date()    // "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"
[] + []           // ""
[1] + [2]         // "12"
{} + {}           // "[object Object][object Object]" ¹
{a:1} + {b:2}     // "[object Object][object Object]" ¹
[1, {}] + [2, {}] // "1,[object Object]2,[object Object]"

¹:請注意,每一行都在表達式上下文中進行計算。 第一個{ ... }是一個對象文字,而不是一個塊,就像在語句上下文中的情況一樣。 在 REPL 中,您可能會看到{} + {}導致NaN ,因為大多數 REPL 在語句上下文中運行; 這里,第一個{}是一個block ,代碼等價於{}; +{}; {}; +{}; ,最后的表達式語句(其值成為完成記錄的結果)是NaN因為一元+將對象強制轉換為數字。

一步一步地, +將值轉換為一個數字,如果你添加到一個空數組+[] ...因為它是空的並且等於0 ,它將

所以從那里開始,現在看看你的代碼,它是++[[]][+[]]+[+[]] ...

它們之間有加號++[[]][+[]] + [+[]]

所以這些[+[]]將返回[0]因為它們有一個空數組,它在另一個數組中被轉換為0 ...

所以想象一下,第一個值是一個二維數組,里面有一個數組......所以[[]][+[]]將等於[[]][0]它將返回[] ...

最后++將其轉換並增加到1 ...

所以你可以想象, 1 + "0"將是"10" ...

為什么返回字符串“10”?

  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
++[[]][+[]]+[+[]]
             ^^^
             |
             v
++[[]][+[]]+[0]
       ^^^
       |
       v
++[[]][0]+[0]
  ^^^^^^^
  |
  v
++[]+[0]
     ^^^
     |
     v
++[]+"0"
^^^^
|
v
++0+"0"
^^^
|
v
1+"0"
^^^^^
|
v
"10"

+運算符通過.valueOf()任何非數字操作數。 如果那不返回數字,則調用.toString()

我們可以簡單地驗證這一點:

 const x = [], y = []; x.valueOf = () => (console.log('x.valueOf() has been called'), y.valueOf()); x.toString = () => (console.log('x.toString() has been called'), y.toString()); console.log(`+x -> ${+x}`);

所以+[]與將""強制轉換為0的數字相同。

如果任何操作數是字符串,則+連接。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM