JavaScriptで2 == [2]になるのはなぜですか?


164

私は最近それ2 == [2]をJavaScript で発見しました。結局のところ、この奇妙な点にはいくつか興味深い結果があります。

var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true

同様に、次の作品:

var a = { "abc" : 1 };
a[["abc"]] === a["abc"]; // this is also true

さらに奇妙なことに、これもうまくいきます:

[[[[[[[2]]]]]]] == 2; // this is true too! WTF?

これらの動作は、すべてのブラウザで一貫しています。

これが言語機能である理由は何ですか?

この「機能」のより異常な結果は次のとおりです。

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

var a = [0];
a == a // true
a == !a // also true, WTF?

これらの例はjimbojwによって発見されたhttp://jimbojw.com名声だけでなく、walkingeyerobot

回答:


134

比較アルゴリズムはECMA仕様で調べることができます(問題のECMA-262、第3版の関連セクション:11.9.3、9.1、8.6.2.6)。

関連する抽象アルゴリズムをJSに変換すると、評価時に何が起こるか2 == [2]は基本的に次のようになります。

2 === Number([2].valueOf().toString())

ここvalueOf()で、arrayは配列自体を返し、1要素の配列の文字列表現は単一の要素の文字列表現です。

これも3番目の例を説明する[[[[[[[2]]]]]]].toString()だけで、文字列だけ2です。

ご覧のとおり、かなり多くの舞台裏の魔法が関わってい===ます。そのため、通常は厳密な等価演算子のみを使用しています。

プロパティ名は常に文字列であるため、最初と2番目の例の方がわかりやすいので、

a[[2]]

に相当

a[[2].toString()]

それはちょうどです

a["2"]

配列マジックが発生する前に、数値キーでさえプロパティ名(文字列)として扱われることに注意してください。


10

これは、==演算子の暗黙的な型変換が原因です。

[2]は、数値と比較すると、数値は2に変換されます。+[2]で単項演算子を試してください。

> +[2]
2

[2]が文字列に変換されると言う人もいます。+"2"また、数2である
dlamblin

1
実際、それは簡単なことではありません。[2]はより文字列に変換されますが、ecma-international.org / ecma
neo

10
var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true

方程式の右側には、値2の数値型を返すa [2]があります。左側では、最初に、2の単一オブジェクトを持つ新しい配列を作成しています。次に、a [(配列はここにあります)]。これが文字列と数値のどちらに評価されるかわかりません。2、または「2」。最初に文字列のケースを取り上げましょう。a ["2"]は新しい変数を作成してnullを返すと思います。null!== 2.では、実際に暗黙的に数値に変換していると仮定しましょう。a [2]は2を返します。2(および===が機能する)と値の2と2の一致 a [value]は文字列または数値を期待するため、配列を暗黙的に数値に変換していると思います。数値が優先されるようです。

余談ですが、その優先順位は誰が決めるのでしょうか。[2]は最初のアイテムとして数値を持っているため、数値に変換されるのですか?それとも、配列をa [array]に渡すときに、配列を最初に数値に変換し、次に文字列に変換しようとするものですか。知るか?

var a = { "abc" : 1 };
a[["abc"]] === a["abc"];

この例では、abcというメンバーを持つaというオブジェクトを作成しています。方程式の右辺はかなり単純です。これはa.abcと同等です。これは1を返します。左側は最初に["abc"]のリテラル配列を作成します。次に、新しく作成された配列を渡して、aオブジェクトの変数を検索します。これは文字列を想定しているため、配列を文字列に変換します。これは、a ["abc"]と評価され、1に等しくなります。1と1は同じ型(===が機能する理由)であり、等しい値です。

[[[[[[[2]]]]]]] == 2; 

これは暗黙的な変換です。===はタイプの不一致があるため、この状況では機能しません。


優先順位に関する質問への回答:配列に==適用さToPrimitive()れ、次にそのtoString()メソッドが呼び出されるため、実際に比較するのは数値2と文字列"2"です。文字列と数値の比較は、文字列を変換することによって行われます
Christoph

8

以下のために==、なぜ場合、これはダグクロックフォードは、常に使用することをお勧めします===。暗黙の型変換は行いません。

の例で===は、等価演算子が呼び出される前に暗黙的な型変換が行われます。


7
[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

それは興味深いです。[0]が真と偽の両方であるということではありません。実際

[0] == true // false

これは、JavaScriptのif()演算子を処理する面白い方法です。


4
実際、これは面白い方法==です。実際の明示的なキャスト(Boolean([0])またはまたは!![0])を使用する[0]true、ブールコンテキストで評価されるはずです。JSでは、すべてのオブジェクトが考慮されますtrue
Christoph

6

1つのアイテムの配列は、アイテム自体として扱うことができます。

これはアヒルのタイピングによるものです。"2" == 2 == [2]なので、おそらくそれ以上。


4
タイプが一致しないためです。最初の例では、左側が最初に評価され、型のマッチングが行われます。
Shawn、

8
また、ここではダックタイピングが正しい言葉だとは思いません。==比較する前に演算子によって実行される暗黙の型変換を行う方が多くなります。
Chetan S

14
これはダックタイピングとは関係ありませんが、弱いタイピング、つまり暗黙的な型変換とは関係ありません
Christoph

@チェタン:彼が言ったこと;)
クリストフ

2
チェタンとクリストフが言ったこと。
Tim Down

3

比較するとき、他の回答に少しディテールを追加するには... ArrayNumberは、JavaScriptを変換しますArrayparseFloat(array)。コンソール(FirebugやWeb Inspectorなど)で自分で試して、さまざまなArray値がどのように変換されるかを確認できます。

parseFloat([2]); // 2
parseFloat([2, 3]); // 2
parseFloat(['', 2]); // NaN

の場合Array、の最初のメンバーに対してparseFloat操作を実行しArray、残りを破棄します。

編集:クリストフの詳細によれば、内部では長い形式を使用している可能性がありますが、結果は常にと同じであるためparseFloatparseFloat(array)変換方法を確認するために常に省略形として使用できます。


2

すべてのケースで2つのオブジェクトを比較しています。==を使用しないでください。比較を考えている場合、==ではなく===を念頭に置いています。==多くの場合、非常識な効果が得られます。言語の良い部分を探してください:)


0

質問の編集セクションの説明:

最初の例

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

最初のタイプキャスト[0]は、上記のクリストフの回答に従って、プリミティブ値に "0"([0].valueOf().toString()

"0" == false

次に、Boolean(false)をNumberに型キャストし、次にString( "0")をNumberに型キャストします。

Number("0") == Number(false)
or  0 == 0 
so, [0] == false  // true

ためのようifであれば条件自体に明示的な比較がない場合のステートメント、条件がために評価されtruthy値。

偽の6つしかありません:false、null、未定義、0、NaN、空の文字列 ""。そして、偽の値ではないものは真実の値です。

[0]は偽の値ではないため、真の値なので、ifステートメントはtrueと評価され、ステートメントを実行します。


2番目の例

var a = [0];
a == a // true
a == !a // also true, WTF?

もう一度値をプリミティブにキャストして、

    a = a
or  [0].valueOf().toString() == [0].valueOf().toString()
or  "0" == "0" // true; same type, same value


a == !a
or  [0].valueOf().toString() == [0].valueOf().toString()
or  "0" == !"0"
or  "0" == false
or  Number("0") == Number(false)
or  0 = 0   // true
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.