++ [[]] [+ []] + [+ []]が文字列「10」を返すのはなぜですか?


1657

これは有効で"10"、JavaScriptで文字列を返します(他の例はこちら):

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

どうして?ここで何が起きてるの?


446
+[]空の配列を0...にキャストすることを理解することから始めて、次に午後を無駄にしてください;)
deceze


10
wtfjs.comを見てください。explanatinosを使用すると、このようなことがかなりあります。
ThiefMaster 2011

3
@deceze、あなたはその種のものをどこで学びますか?どの本?私はMDNからJSを学んでいますが、彼らはこれらのことを教えていません
Siddharth Thevaril '23

6
@SiddharthThevarilあなたがしたのと同じように:誰かがどこかでそれについて投稿し、たまたまそれを読んだ。
だます

回答:


2071

分割すると、混乱は次のようになります。

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

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は次のように評価されます。

  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 [[DefaultValue]](ヒント)

ヒント文字列を使用してOの[[DefaultValue]]内部メソッドが呼び出されると、次の手順が実行されます。

  1. toStringを、オブジェクト "O"の[[Get]]内部メソッドを引数 "toString"で呼び出した結果とします。

  2. IsCallable(toString)がtrueの場合、

a。strをtoStringの[[Call]]内部メソッドを呼び出した結果とし、Oをthis値として、空の引数リストを指定します。

b。strがプリミティブ値の場合、strを返します。

.toString配列のは言います:

15.4.4.2 Array.prototype.toString()

toStringメソッドが呼び出されると、次の手順が実行されます。

  1. この値でToObjectを呼び出した結果をarrayとします。

  2. funcを、配列の[[Get]]内部メソッドを引数「join」で呼び出した結果とします。

  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


8
@harper:これは厳密な等価チェッカーです。つまりtrue、値と型の両方が同じ場合にのみ戻ります。(型変換後も同じ)を0 == ""返しますが、true(同じ型で0 === ""はありfalseません)。
pimvdb 2011

41
これの一部は正しくありません。前置()演算子は常に数値を返すため1 + [0]、式はにではなくに要約されます。bclary.com/2004/11/07/#a-11.4.4を参照してください"1" + [0]++
Tim Down

6
@ティムダウン:あなたは完全に正しいです。私はこれを修正しようとしていますが、修正しようとしたときに別のものが見つかりました。これがどのようにして可能かはわかりません。++[[]][0]確か1にを返しますが++[]、エラーをスローします。これはに++[[]][0]煮詰められるように見えるので、注目に値し++[]ます。なぜ++[]エラーをスローするのにエラーをスロー++[[]][0]しないのか、おそらく何か考えがありますか?
pimvdb

11
@pimvdb:問題は前置操作のPutValue呼び出し(ES3用語では8.7.2)にあると確信しています。PutValue参照が必要ですが、[]式自体は参照を生成しません。変数参照を含む式(たとえば、以前に定義したvar a = []後で++a機能する)またはオブジェクトのプロパティアクセス(など[[]][0])は、参照を生成します。簡単に言うと、前置演算子は値を生成するだけでなく、その値をどこかに置く必要があります。
Tim Down

13
@pimvdb:したがって、を実行するとvar a = []; ++aa1になります。を実行する++[[]][0]と、[[]]式によって作成された配列には、インデックス0の数値1のみが含ま++れるようになります。これを行うには、参照が必要です。
Tim Down

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

次に、文字列を連結します

1+[0].toString() = 10

7
書く===よりも明確にすべきではないでしょう=>か?
Mateen Ulhaq 2018

61

以下は、この質問がまだクローズされている間に私が投稿したこの質問に答えるブログ投稿からの抜粋です。リンクは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"ます。なぜこれが起こるのかは読者の練習問題として残されています...


24

簡単にしましょう:

++[[]][+[]]+[+[]] = "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"

13

これは同じに評価されますが、少し小さいです

+!![]+''+(+[])
  • []-配列は変換され、追加または減算すると0に変換されるため、+ [] = 0
  • ![]-falseと評価されるため、!! []はtrueと評価されます
  • + !! []-trueをtrueに評価される数値に変換するため、この場合は1
  • + ''-空の文字列を式に追加して、数値を文字列に変換します
  • + []-0と評価されます

そう評価されます

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

だから今それを手に入れました、これを試してください:

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

まあそれはまだ「10」に評価されます。ただし、これは別の方法で行っています。これをchromeなどのJavaScriptインスペクタで評価してみてください。
Vlad Shlosberg、2011

_ = $ = + []、++ _ + '' + $-> _ = $ = 0、++ _ + '' + $-> _ = 0、$ = 0、++ _ + '' + $ -> ++ 0 + '' + 0-> 1 + '' + 0-> '10' // Yei:v
LeagueOfJava

この1と同じに評価が、それはあなたよりもさらに小さいです:"10"
ADJenks

7

+ []は0に評価されます[...]次に、それを合計(+演算)すると、配列の内容が、コンマで結合された要素で構成される文字列表現に変換されます。

配列のインデックスを取得する(+操作よりも優先度が高い)のような他のものは序数であり、何も興味深いものではありません。


4

おそらく、式を数字なしで "10"に評価する最も短い方法は次のとおりです。

+!+[] + [+[]] //「10」

-~[] + [+[]] //「10」

// ==========説明========== \\

+!+[]+[]0に!0変換しtrueます。に変換します。+true1に変換します -~[]= -(-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]"

1
  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
    

1
答えは混乱/混乱しています、IOWは間違っています。はと同等で[]はありません""。最初に要素が抽出され、次にによって変換され++ます。
PointedEars 2012年

1

その一歩一歩、+値を数値に変え、空の配列に追加すると+[]...空でと等しいので0

だからそこから、今あなたのコードを調べてください、それは++[[]][+[]]+[+[]]...

そしてそれらの間にプラスがあります++[[]][+[]]+[+[]]

したがって、他の配列内に変換される空の配列があるため、これら[+[]]は返さ[0]0ます...

ように想像する、最初の値である2次元そう...つのアレイの内部の配列[[]][+[]]に等しくなる[[]][0]返されました[]...

そして最後++にそれを変換し、それを1...に増やします

想像できるように、1+ "0""10"...

文字列「10」を返すのはなぜですか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.