回答:
分割すると、混乱は次のようになります。
++[[]][+[]]
+
[+[]]
JavaScriptでは、それは本当です+[] === 0
。+
何かを数値に変換します。この場合、それは+""
またはになります0
(以下の仕様の詳細を参照)。
したがって、それを簡略化することができます(++
に優先します+
)。
++[[]][0]
+
[0]
の[[]][0]
意味:最初の要素をから取得する[[]]
ことは、
[[]][0]
内部配列([]
)を返します。参照のために言うのは間違っています[[]][0] === []
がA
、間違った表記を避けるために内部配列を呼び出しましょう。
++
オペランドの前は、「1ずつインクリメントし、インクリメントした結果を返す」という意味です。だから、++[[]][0]
同等でありNumber(A) + 1
(または+A + 1
)。
繰り返しますが、混乱を単純化してより読みやすいものにすることができます。レッツ・代替[]
のためのバックA
:
(+[] + 1)
+
[0]
before +[]
が配列を数値0
に強制変換できるようにするには""
、まず文字列に強制変換する必要があります。つまり、再びです。最後に、1
が追加され、結果はになり1
ます。
(+[] + 1) === (+"" + 1)
(+"" + 1) === (0 + 1)
(0 + 1) === 1
さらに簡単にしましょう:
1
+
[0]
また、これはJavaScript:にも当てはまります。これは[0] == "0"
、1つの要素で配列を結合しているためです。結合すると、で区切られた要素が連結され,
ます。1つの要素で、このロジックが最初の要素自体になると推測できます。
この場合、+
2つのオペランド、数値と配列が表示されます。現在、2つを同じ型に強制変換しようとしています。最初に、配列が文字列"0"
に強制変換されます。次に、数値が文字列に強制変換されます("1"
)。数値+
文字===
列文字列。
"1" + "0" === "10" // Yay!
仕様詳細+[]
:
これはかなり迷路ですが+[]
、最初に文字列に変換され+
ます。
11.4.6単項+演算子
単項+演算子は、オペランドを数値型に変換します。
プロダクションUnaryExpression:+ UnaryExpressionは次のように評価されます。
exprをUnaryExpressionの評価結果とします。
ToNumber(GetValue(expr))を返します。
ToNumber()
言う:
オブジェクト
次の手順を適用します。
primValueをToPrimitive(入力引数、ヒント文字列)にします。
ToString(primValue)を返します。
ToPrimitive()
言う:
オブジェクト
オブジェクトのデフォルト値を返します。オブジェクトのデフォルト値を取得するには、オブジェクトの[[DefaultValue]]内部メソッドを呼び出し、オプションのヒントPreferredTypeを渡します。[[DefaultValue]]内部メソッドの動作は、8.12.8のすべてのネイティブECMAScriptオブジェクトのこの仕様で定義されています。
[[DefaultValue]]
言う:
8.12.8 [[DefaultValue]](ヒント)
ヒント文字列を使用してOの[[DefaultValue]]内部メソッドが呼び出されると、次の手順が実行されます。
toStringを、オブジェクト "O"の[[Get]]内部メソッドを引数 "toString"で呼び出した結果とします。
IsCallable(toString)がtrueの場合、
a。strをtoStringの[[Call]]内部メソッドを呼び出した結果とし、Oをthis値として、空の引数リストを指定します。
b。strがプリミティブ値の場合、strを返します。
.toString
配列のは言います:
15.4.4.2 Array.prototype.toString()
toStringメソッドが呼び出されると、次の手順が実行されます。
この値でToObjectを呼び出した結果をarrayとします。
funcを、配列の[[Get]]内部メソッドを引数「join」で呼び出した結果とします。
IsCallable(func)がfalseの場合、funcを標準の組み込みメソッドObject.prototype.toString(15.2.4.2)にします。
この値と空の引数リストとして配列を提供するfuncの[[Call]]内部メソッドを呼び出した結果を返します。
そう+[]
まで来る+""
ので、[].join() === ""
。
再び、+
は次のように定義されます。
11.4.6単項+演算子
単項+演算子は、オペランドを数値型に変換します。
プロダクションUnaryExpression:+ UnaryExpressionは次のように評価されます。
exprをUnaryExpressionの評価結果とします。
ToNumber(GetValue(expr))を返します。
ToNumber
次の""
ように定義されます:
StringNumericLiteral ::: [empty]のMVは0です。
したがって+"" === 0
、したがって+[] === 0
。
true
、値と型の両方が同じ場合にのみ戻ります。(型変換後も同じ)を0 == ""
返しますが、true
(同じ型で0 === ""
はありfalse
ません)。
1 + [0]
、式はにではなくに要約されます。bclary.com/2004/11/07/#a-11.4.4を参照してください"1" + [0]
++
++[[]][0]
確か1
にを返しますが++[]
、エラーをスローします。これはに++[[]][0]
煮詰められるように見えるので、注目に値し++[]
ます。なぜ++[]
エラーをスローするのにエラーをスロー++[[]][0]
しないのか、おそらく何か考えがありますか?
PutValue
呼び出し(ES3用語では8.7.2)にあると確信しています。PutValue
参照が必要ですが、[]
式自体は参照を生成しません。変数参照を含む式(たとえば、以前に定義したvar a = []
後で++a
機能する)またはオブジェクトのプロパティアクセス(など[[]][0]
)は、参照を生成します。簡単に言うと、前置演算子は値を生成するだけでなく、その値をどこかに置く必要があります。
var a = []; ++a
、a
1になります。を実行する++[[]][0]
と、[[]]
式によって作成された配列には、インデックス0の数値1のみが含ま++
れるようになります。これを行うには、参照が必要です。
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]
次に、文字列を連結します
1+[0].toString() = 10
===
よりも明確にすべきではないでしょう=>
か?
以下は、この質問がまだクローズされている間に私が投稿したこの質問に答えるブログ投稿からの抜粋です。リンクはECMAScript 3仕様(のHTMLコピー)へのリンクであり、現在でも一般的に使用されているWebブラウザーでのJavaScriptのベースラインです。
まず、コメント:この種の式は(正気な)本番環境では決して表示されず、読者がJavaScriptのダーティエッジをどれだけよく理解しているかの練習としてのみ使用できます。一般的な変換の一部と同様に、JavaScript演算子が型間で暗黙的に変換するという一般的な原則は役立ちますが、この場合の詳細の多くは役に立ちません。
式++[[]][+[]]+[+[]]
は最初は堂々として不明瞭に見えますが、実際には比較的簡単に別の式に分解できます。以下では、わかりやすくするために括弧を追加しました。彼らが何も変更しないことを保証できますが、それを確認したい場合は、グループ化演算子について自由に読んでください。したがって、式は次のように明確に記述できます。
( ++[[]][+[]] ) + ( [+[]] )
これを分解すると、がに+[]
評価されることを観察することで単純化でき0
ます。これが真である理由を満足させるには、単項+演算子を確認し、ToPrimitiveが空の配列を空の文字列に変換し0
て、最後にToNumberによって変換される、少し曲がりくねった道をたどります。これで0
、の各インスタンスを置き換えることができます+[]
。
( ++[[]][0] ) + [0]
すでに簡単です。については++[[]][0]
、それはプレフィックス増分演算子(++
)、それ自体が空の配列である単一の要素を持つ配列を定義する配列リテラル([[]]
)、および配列リテラルによって定義された配列で呼び出されるプロパティアクセサー([0]
)の組み合わせです。
だから、私たちは単純化[[]][0]
することができ、私[]
たちは持ってい++[]
ますよね?実際、評価++[]
はエラーをスローするため、これは当てはまりません。ただし、の性質について少し考えると、++
これが明らかになります。変数(たとえば++i
)またはオブジェクトプロパティ(たとえば++obj.count
)をインクリメントするために使用されます。値を評価するだけでなく、その値をどこかに保存します。の場合、++[]
更新するオブジェクトプロパティまたは変数への参照がないため、新しい値を(それが何であれ)配置する場所がありません。仕様では、これは、プレフィックスのインクリメント演算子によって呼び出される内部のPutValue操作によってカバーされます。
それでは、何を++[[]][0]
するのでしょうか?まあ、と同様のロジックにより+[]
、内部配列はに変換され0
、この値はによって増分さ1
れ、最終的な値がになり1
ます。0
外部配列のプロパティの値はに更新され1
、式全体がに評価され1
ます。
これは私たちに残します
1 + [0]
...これは加算演算子の簡単な使い方です。両方のオペランドが最初にプリミティブに変換され、いずれかのプリミティブ値が文字列の場合は文字列連結が実行され、それ以外の場合は数値の加算が実行されます。[0]
に変換される"0"
ため、文字列連結が使用され、が生成され"10"
ます。
最後の余談として、オブジェクトのプリミティブ値への変換時に両方がチェックされて使用されるため、toString()
またはのvalueOf()
メソッドのいずれかをオーバーライドするArray.prototype
と、式の結果が変わることはすぐにはわかりません。たとえば、次の
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"
だから今それを手に入れました、これを試してください:
_=$=+[],++_+''+$
"10"
おそらく、式を数字なしで "10"に評価する最も短い方法は次のとおりです。
+!+[] + [+[]]
//「10」
-~[] + [+[]]
//「10」
// ==========説明========== \\
+!+[]
:+[]
0に!0
変換しtrue
ます。に変換します。+true
1に変換します
-~[]
= -(-1)
1です
[+[]]
:+[]
0に変換し[0]
ます。は、単一の要素0を持つ配列です。
次に、JSは1 + [0]
、つまりNumber + Array
式を評価します。次に、ECMA仕様が機能します。+
演算子toString()/valueOf()
は、ベースObject
プロトタイプから関数を呼び出すことにより、両方のオペランドを文字列に変換します。式の両方のオペランドが数値のみの場合は、追加関数として動作します。トリックは、配列が要素を簡単に連結された文字列表現に変換することです。
いくつかの例:
1 + {} // "1[object Object]"
1 + [] // "1"
1 + new Date() // "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"
2つのObjects
追加の結果が次のようになるという素晴らしい例外がありますNaN
。
[] + [] // ""
[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次元そう...つのアレイの内部の配列[[]][+[]]
に等しくなる[[]][0]
返されました[]
...
そして最後++
にそれを変換し、それを1
...に増やします
想像できるように、1
+ "0"
は"10"
...
+[]
空の配列を0
...にキャストすることを理解することから始めて、次に午後を無駄にしてください;)