分割すると、混乱は次のようになります。
++[[]][+[]]
+
[+[]]
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つの要素を持つ配列を結合するためです。結合は,
で区切られた要素を連結します。 1つの要素があれば、このロジックによって最初の要素自体が生成されることになります。
したがって、最後に(number + string = string)を取得します。
1
+
"0"
=== "10" // Yay!
+[]
の仕様詳細
これはかなりの迷路ですが、+[]
を行うには、まず+
が言っていることなので、まず文字列に変換します。
11.4.6単項演算子+
単項演算子+は、そのオペランドを数値型に変換します。
プロダクションUnaryExpression:+ UnaryExpressionは次のように評価されます。
UnaryExpressionを評価した結果をexprとする。
ToNumber(GetValue(expr))を返します。
ToNumber()
は言う:
対象
以下の手順に従ってください。
PrimValueをToPrimitive(入力引数、ヒント文字列)とします。
ToString(primValue)を返します。
ToPrimitive()
は言う:
対象
Objectのデフォルト値を返します。オブジェクトのデフォルト値は、オブジェクトの[[DefaultValue]]内部メソッドを呼び出し、オプションのヒントPreferredTypeを渡すことによって取得されます。 [[DefaultValue]]内部メソッドの動作は、8.12.8のすべてのネイティブECMAScriptオブジェクトに対して、この仕様で定義されています。
[[DefaultValue]]
は言う:
8.12.8 [[DefaultValue]](ヒント)
ヒント文字列でOの[[DefaultValue]]内部メソッドが呼び出されると、次の手順が実行されます。
ToStringを引数 "toString"でオブジェクトOの[[Get]]内部メソッドを呼び出した結果とする。
IsCallable(toString)がtrueの場合、
a。 strをtoStringの[[Call]]内部メソッドを呼び出した結果で、Oにthisの値を指定し、空の引数リストを指定します。
b。 strがプリミティブ値の場合、strを返します。
配列の.toString
は次のように言っています。
15.4.4.2 Array.prototype.toString()
ToStringメソッドが呼び出されると、次の手順が実行されます。
この値に対してToObjectを呼び出した結果をarrayとします。
引数「join」を指定してarrayの[[Get]]内部メソッドを呼び出した結果をfuncとする。
IsCallable(func)がfalseの場合、funcを標準の組み込みメソッドObject.prototype.toString(15.2.4.2)とします。
This値および空の引数リストとして、arrayを提供するfuncの[[Call]]内部メソッドを呼び出した結果を返します。
だから+[]
は+""
になります、なぜなら[].join() === ""
だからです。
繰り返しますが、+
は次のように定義されています。
11.4.6単項演算子+
単項演算子+は、そのオペランドを数値型に変換します。
プロダクションUnaryExpression:+ UnaryExpressionは次のように評価されます。
UnaryExpressionを評価した結果をexprとする。
ToNumber(GetValue(expr))を返します。
ToNumber
は""
に対して次のように定義されています。
StringNumericLiteral ::: [empty]のMVは0です。
だから+"" === 0
、そして+[] === 0
。
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]
それから文字列の連結があります
1+[0].toString() = 10
以下は、この質問がまだ閉じられている間に私が投稿したこの質問に答える blog投稿 からの引用です。リンクはECMAScript 3仕様(のHTMLコピー)へのリンクであり、今日の一般的に使用されているWebブラウザにおけるJavaScriptのベースラインです。
まず、コメントです。この種の表現は、どのような(正しい)プロダクション環境でも表示されることはなく、読者がJavaScriptのダーティなエッジをどれだけよく知っているかを知るための演習としてのみ役立ちます。 JavaScript演算子が暗黙的に型間で変換するという一般的な原則は、一般的な変換の一部と同様に便利ですが、この場合の詳細の多くはそうではありません。
式++[[]][+[]]+[+[]]
は最初はかなり印象的であいまいに見えますが、実際は比較的簡単に別々の式に分割されます。以下に、わかりやすくするために括弧を追加しました。私はあなたが彼らが何も変更しないことを保証することができます、しかしあなたがそれを確かめたいならば grouping演算子 について読んで自由に感じなさい。したがって、式はより明確に次のように書くことができます。
( ++[[]][+[]] ) + ( [+[]] )
これを分解すると、+[]
が0
に評価されることを観察することによって単純化できます。これが本当である理由を自分で満足させるために、 unary + operator を調べ、最後に ToPrimitive で空の配列を空の文字列に変換し、最後に0
に変換してください。 ToNumber 。 0
の各インスタンスを+[]
に置き換えることができます。
( ++[[]][0] ) + [0]
もうもっとシンプル。 ++[[]][0]
に関しては、それは 接頭辞増加演算子 (++
)、それ自身が空の配列([[]]
)である単一の要素を持つ配列を定義する 配列リテラル 、および property accessor の組み合わせです。 ([0]
)が配列リテラルで定義された配列に対して呼び出されました。
それで、私たちは[[]][0]
を[]
に単純化することができます、そして、私達は++[]
を持っていますね。実際のところ、++[]
を評価するとエラーが発生するため、実際にはそうではありません。ただし、++
の性質について少し考えてみると、変数(++i
など)またはオブジェクトプロパティ(++obj.count
など)の増分に使用されています。値を評価するだけでなく、その値をどこかに格納します。 ++[]
の場合、更新するオブジェクトのプロパティや変数への参照がないため、新しい値を(どこにあろうと)置くことはできません。仕様的には、これは内部の PutValue 操作でカバーされています。これは、接頭辞のインクリメント演算子によって呼び出されます。
それでは、++[[]][0]
は何をするのでしょうか。さて、+[]
と同じようなロジックによって、内側の配列は0
に変換され、この値は1
だけインクリメントされて1
の最終値が得られます。外部配列のプロパティ0
の値が1
に更新され、式全体が1
に評価されます。
これは私たちを残します
1 + [0]
...これは Addition演算子 の簡単な使い方です。両方のオペランドが最初に プリミティブ に変換され、いずれかのプリミティブ値が文字列の場合は文字列連結が実行され、それ以外の場合は数値の加算が実行されます。 [0]
は"0"
に変換されるので、文字列連結が使用され、"10"
が生成されます。
最後の余談として、Array.prototype
のtoString()
メソッドまたは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"
これは同じだが少し小さいと評価される
+!![]+''+(+[])
に評価されます
+(true) + '' + (0)
1 + '' + 0
"10"
だから今あなたはそれを持って、これを試してみてください:
_=$=+[],++_+''+$
+ []は0 [...]に評価され、それから何かでそれを合計(+ operation)して、配列の内容をコンマで結合された要素からなる文字列表現に変換します。
配列のインデックスをとること(+操作よりも優先度が高い)のような他のものは序数であり、面白いものではありません。
おそらく、式を数字なしで "10"に評価するための最短の方法は、次のとおりです。
+!+[] + [+[]]
// "10"
-~[] + [+[]]
// "10"
// ==========説明============
+!+[]
:+[]
0に変換します。!0
はtrue
に変換します。 +true
は1に変換されます。-~[]
= -(-1)
は1です。
[+[]]
:+[]
0に変換します。[0]
は、単一の要素0を持つ配列です。
その後、JSは1 + [0]
、つまりNumber + Array
式を評価します。その後、ECMA仕様が機能します。+
演算子は、基本Object
プロトタイプからtoString()/valueOf()
関数を呼び出すことによって、両方のオペランドを文字列に変換します。式の両方のオペランドが数値のみの場合、これは加算関数として機能します。トリックは、配列がそれらの要素を簡単に連結された文字列表現に変換することです。
いくつかの例:
1 + {} // "1[object Object]"
1 + [] // "1"
1 + new Date() // "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"
2つのObjects
の追加がNaN
になるというNice例外があります。
[] + [] // ""
[1] + [2] // "12"
{} + {} // NaN
{a:1} + {b:2} // NaN
[1, {}] + [2, {}] // "1,[object Object]2,[object Object]"
+ ''または+ []は0を評価します。
++[[]][+[]]+[+[]] = 10
++[''][0] + [0] : First part is gives zeroth element of the array which is empty string
1+0
10
ステップバイステップで、+
は値を数値に変換し、空の配列に追加した場合+[]
...は空で0
と等しいので、
だからそこから、今あなたのコードを見て、それは++[[]][+[]]+[+[]]
...です。
そしてそれらの間にプラスがあります++[[]][+[]]
+ [+[]]
したがって、これらの[+[]]
は空の配列を持っているので[0]
を返します。これは他の配列の内側で0
に変換されます.
想像のとおり、最初の値は 2次元 配列で、内部に1つの配列があります。したがって、[[]][+[]]
は[[]][0]
と等しくなり、[]
...が返されます。
そして最後に++
に変換して1
に増やします。
1
+ "0"
は"10"
...になるでしょう。