Warum ist 2 == [2] in JavaScript?

164

Ich habe das kürzlich 2 == [2]in JavaScript entdeckt. Wie sich herausstellt, hat diese Eigenart einige interessante Konsequenzen:

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

Ebenso funktioniert folgendes:

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

Noch seltsamer, das funktioniert auch:

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

Diese Verhaltensweisen scheinen in allen Browsern konsistent zu sein.

Irgendeine Idee, warum dies eine Sprachfunktion ist?

Hier sind weitere verrückte Konsequenzen dieser "Funktion":

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

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

Diese Beispiele wurden von jimbojw http://jimbojw.com Fame sowie walkingeyerobot gefunden .

Xavi
quelle

Antworten:

134

Sie können den Vergleichsalgorithmus in der ECMA-Spezifikation nachschlagen (relevante Abschnitte von ECMA-262, 3. Ausgabe für Ihr Problem: 11.9.3, 9.1, 8.6.2.6).

Wenn Sie die beteiligten abstrakten Algorithmen zurück in JS übersetzen, geschieht bei der Bewertung 2 == [2]im Wesentlichen Folgendes:

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

Dabei gibt valueOf()for for Arrays das Array selbst zurück und die Zeichenfolgendarstellung eines Ein-Element-Arrays ist die Zeichenfolgendarstellung des einzelnen Elements.

Dies erklärt auch das dritte Beispiel, da [[[[[[[2]]]]]]].toString()es immer noch nur die Zeichenfolge ist 2.

Wie Sie sehen, gibt es eine Menge Magie hinter den Kulissen, weshalb ich im Allgemeinen nur den strengen Gleichheitsoperator verwende ===.

Das erste und zweite Beispiel sind einfacher zu befolgen, da Eigenschaftsnamen immer Zeichenfolgen sind

a[[2]]

ist äquivalent zu

a[[2].toString()]

das ist nur

a["2"]

Beachten Sie, dass auch numerische Schlüssel als Eigenschaftsnamen (dh Zeichenfolgen) behandelt werden, bevor Array-Magie auftritt.

Christoph
quelle
10

Dies liegt an der impliziten Typkonvertierung des ==Operators.

[2] wird in eine Zahl umgewandelt, die im Vergleich zu einer Zahl 2 ist. Versuchen Sie den unären +Operator auf [2].

> +[2]
2
Chetan S.
quelle
Andere sagen, [2] sei in einen String konvertiert. +"2"ist auch die Nummer 2.
Dlamblin
1
Eigentlich ist es nicht so einfach. [2] wird in String konvertiert wäre näher, aber werfen Sie
neo
10
var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true

Auf der rechten Seite der Gleichung haben wir das a [2], das einen Zahlentyp mit dem Wert 2 zurückgibt. Auf der linken Seite erstellen wir zuerst ein neues Array mit einem einzelnen Objekt von 2. Dann rufen wir ein [( Array ist hier drin)]. Ich bin nicht sicher, ob dies zu einer Zeichenfolge oder einer Zahl führt. 2 oder "2". Nehmen wir zuerst den String-Fall. Ich glaube, eine ["2"] würde eine neue Variable erstellen und null zurückgeben. null! == 2. Nehmen wir also an, es wird tatsächlich implizit in eine Zahl konvertiert. a [2] würde 2 zurückgeben. 2 und 2 stimmen in Typ (also === funktioniert) und Wert überein. Ich denke, es konvertiert implizit das Array in eine Zahl, weil ein [Wert] eine Zeichenfolge oder Zahl erwartet. Es sieht so aus, als hätte die Zahl einen höheren Vorrang.

Nebenbei frage ich mich, wer diesen Vorrang bestimmt. Liegt es daran, dass [2] eine Nummer als erstes Element hat und daher in eine Nummer konvertiert wird? Oder ist es so, dass beim Übergeben eines Arrays an ein [Array] versucht wird, das Array zuerst in eine Zahl und dann in eine Zeichenfolge umzuwandeln? Wer weiß?

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

In diesem Beispiel erstellen Sie ein Objekt namens a mit einem Element namens abc. Die rechte Seite der Gleichung ist ziemlich einfach; es ist äquivalent zu a.abc. Dies gibt 1 zurück. Die linke Seite erstellt zuerst ein Literal-Array von ["abc"]. Anschließend suchen Sie nach einer Variablen für ein Objekt, indem Sie das neu erstellte Array übergeben. Da dies eine Zeichenfolge erwartet, wird das Array in eine Zeichenfolge konvertiert. Dies ergibt nun ein ["abc"], das gleich 1 ist. 1 und 1 sind vom gleichen Typ (weshalb === funktioniert) und gleichwertig.

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

Dies ist nur eine implizite Konvertierung. === würde in dieser Situation nicht funktionieren, da eine Typinkongruenz vorliegt.

Shawn
quelle
Die Antwort auf Ihre Frage zur Priorität: ==gilt ToPrimitive()für das Array, das wiederum seine toString()Methode aufruft. Sie vergleichen also tatsächlich die Zahl 2mit der Zeichenfolge "2". Der Vergleich zwischen einer Zeichenfolge und einer Zahl erfolgt durch Konvertieren der Zeichenfolge
Christoph
8

Aus ==diesem Grund empfiehlt Doug Crockford , immer zu verwenden ===. Es wird keine implizite Typkonvertierung durchgeführt.

In den Beispielen mit ===wird die implizite Typkonvertierung durchgeführt, bevor der Gleichheitsoperator aufgerufen wird.

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

Das ist interessant, es ist nicht so, dass [0] tatsächlich sowohl wahr als auch falsch ist

[0] == true // false

Es ist Javascript's lustige Art, if () Operator zu verarbeiten.

Alexander Abramov
quelle
4
Eigentlich ==funktioniert es auf lustige Weise . Wenn Sie eine tatsächliche explizite Besetzung (dh Boolean([0])oder !![0]) verwenden, werden Sie feststellen, dass [0]diese truein booleschen Kontexten ausgewertet wird, wie es sollte: In JS wird jedes Objekt berücksichtigttrue
Christoph
6

Ein Array eines Elements kann als das Element selbst behandelt werden.

Dies ist auf die Eingabe von Enten zurückzuführen. Seit "2" == 2 == [2] und möglicherweise mehr.

Ólafur Waage
quelle
4
weil sie nicht in der Art übereinstimmen. Im ersten Beispiel wird zuerst die linke Seite ausgewertet, und der Typ stimmt überein.
Shawn
8
Ich denke auch nicht, dass Entenschreiben hier das richtige Wort ist. Dies hängt eher mit der impliziten Typkonvertierung zusammen, die der ==Operator vor dem Vergleich durchführt.
Chetan S
14
Dies hat nichts mit Ententypisierung zu tun, sondern mit schwacher Typisierung, dh impliziter Typkonvertierung
Christoph
@Chetan: was er gesagt hat;)
Christoph
2
Was Chetan und Christoph gesagt haben.
Tim Down
3

Um den anderen Antworten ein kleines Detail hinzuzufügen ... Beim Vergleich Arrayvon a Numbermit a konvertiert Javascript das Arraymit parseFloat(array). Sie können es selbst in der Konsole versuchen (z. B. Firebug oder Web Inspector), um zu sehen, in welche unterschiedlichen ArrayWerte konvertiert wird.

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

Führt für Arrays parseFloatdie Operation für das Arrayerste Mitglied des und führt den Rest aus.

Bearbeiten: Laut Christophs Details kann es sein, dass das längere Formular intern verwendet wird, aber die Ergebnisse sind durchweg identisch mit parseFloat, sodass Sie immer parseFloat(array)als Kurzform verwenden können, um sicher zu wissen, wie es konvertiert wird.

Augenlidlosigkeit
quelle
2

Sie vergleichen in jedem Fall 2 Objekte. Verwenden Sie nicht ==. Wenn Sie an einen Vergleich denken, denken Sie an === und nicht an ==. == kann oft verrückte Effekte hervorrufen. Suche nach den guten Teilen in der Sprache :)

Jaseem
quelle
0

Erläuterung zum Abschnitt BEARBEITEN der Frage:

1. Beispiel

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

Erste Typumwandlung [0] auf einen primitiven Wert gemäß Christophs Antwort oben haben wir "0" ( [0].valueOf().toString())

"0" == false

Geben Sie nun Boolean (false) in Number und String ("0") in Number ein

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

Was ifAussage, wenn gibt es keine explizite Vergleich im Zustand , wenn sich die Bedingung auswertet für truthy Werte.

Es gibt nur 6 falsche Werte : false, null, undefined, 0, NaN und leere Zeichenfolge "". Und alles, was kein falscher Wert ist, ist ein wahrer Wert.

Da [0] kein Falschwert ist, sondern ein Wahrheitswert, wird die ifAnweisung als wahr ausgewertet und die Anweisung ausgeführt.


2. Beispiel

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

Geben Sie die Werte erneut in primitiv um.

    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
n4m31ess_c0d3r
quelle