Dies ist gültig und gibt die Zeichenfolge "10"
in JavaScript zurück ( weitere Beispiele hier ):
console.log(++[[]][+[]]+[+[]])
Warum? Was passiert hier?
javascript
syntax
SapuSeven
quelle
quelle
+[]
ein leeres Array0
Antworten:
Wenn wir es aufteilen, ist das Chaos gleich:
In JavaScript ist das wahr
+[] === 0
.+
wandelt etwas in eine Zahl um, und in diesem Fall kommt es auf+""
oder an0
(siehe Spezifikationsdetails unten).Daher können wir es vereinfachen (
++
hat Vorrang vor+
):Denn
[[]][0]
bedeutet: Holen Sie sich das erste Element von[[]]
, es ist wahr, dass:[[]][0]
Gibt das innere Array ([]
) zurück. Aufgrund von Referenzen ist es falsch zu sagen[[]][0] === []
, aber nennen wir das innere ArrayA
, um die falsche Notation zu vermeiden.++
vor seinem Operanden bedeutet "um eins erhöhen und das inkrementierte Ergebnis zurückgeben". Ist++[[]][0]
also gleichbedeutend mitNumber(A) + 1
(oder+A + 1
).Auch hier können wir das Durcheinander in etwas Lesbareres vereinfachen. Lassen Sie uns zurück ersetzen
[]
fürA
:Bevor
+[]
das Array in die Zahl0
gezwungen werden kann , muss es zuerst in eine Zeichenfolge gezwungen werden, was""
wiederum der Fall ist . Schließlich1
wird hinzugefügt, was zu führt1
.(+[] + 1) === (+"" + 1)
(+"" + 1) === (0 + 1)
(0 + 1) === 1
Vereinfachen wir es noch weiter:
Dies gilt auch für JavaScript :
[0] == "0"
, da ein Array mit einem Element verbunden wird. Durch das Verknüpfen werden die durch getrennten Elemente verkettet,
. Mit einem Element können Sie ableiten, dass diese Logik zum ersten Element selbst führt.In diesem Fall werden
+
zwei Operanden angezeigt: eine Zahl und ein Array. Es wird jetzt versucht, die beiden zum gleichen Typ zu zwingen. Zuerst wird das Array in die Zeichenfolge gezwungen"0"
, dann wird die Zahl in eine Zeichenfolge ("1"
) gezwungen . Number+
String===
String .Spezifikationsdetails für
+[]
:Dies ist ein ziemliches Labyrinth, aber zu tun
+[]
, zuerst wird es in eine Zeichenfolge konvertiert, weil das+
sagt:ToNumber()
sagt:ToPrimitive()
sagt:[[DefaultValue]]
sagt:Das
.toString
eines Arrays sagt:Also
+[]
kommt es darauf an+""
, weil[].join() === ""
.Wieder ist das
+
definiert als:ToNumber
ist definiert""
als:So
+"" === 0
und so+[] === 0
.quelle
true
wenn sowohl Wert als auch Typ identisch sind.0 == ""
Erträgetrue
(gleich nach Typumwandlung), aber0 === ""
istfalse
(nicht gleiche Typen).1 + [0]
, nicht"1" + [0]
, weil der++
Operator prefix ( ) immer eine Zahl zurückgibt. Siehe bclary.com/2004/11/07/#a-11.4.4++[[]][0]
kehrt zwar zurück1
,++[]
wirft aber einen Fehler aus. Das ist bemerkenswert, weil es so aussieht, als würde++[[]][0]
es darauf hinauslaufen++[]
. Haben Sie vielleicht eine Idee, warum++[]
ein Fehler ausgelöst++[[]][0]
wird, während dies nicht der Fall ist?PutValue
Aufruf (in der ES3-Terminologie 8.7.2) in der Präfixoperation liegt.PutValue
erfordert eine Referenz, während[]
ein Ausdruck für sich genommen keine Referenz erzeugt. Ein Ausdruck, der eine Variablenreferenz enthält (sagen wir, wir haben zuvor definiert, funktioniertvar a = []
dann++a
) oder den Eigenschaftszugriff auf ein Objekt (z. B.[[]][0]
), erzeugt eine Referenz. Einfacher ausgedrückt erzeugt der Präfixoperator nicht nur einen Wert, sondern benötigt auch einen Platz, um diesen Wert zu setzen.var a = []; ++a
,a
ist 1. Nach der Ausführung++[[]][0]
wird die Anordnung durch den erstellte[[]]
Ausdruck enthält jetzt nur die Nummer 1 bei Index 0++
erfordert eine Referenz , dies zu tun.Dann haben wir eine String-Verkettung
quelle
===
als=>
?Das Folgende ist aus einem Blog-Beitrag übernommen , der diese Frage beantwortet, die ich gepostet habe, als diese Frage noch geschlossen war. Links verweisen auf (eine HTML-Kopie von) der ECMAScript 3-Spezifikation, die in den heute häufig verwendeten Webbrowsern immer noch die Basis für JavaScript darstellt.
Zunächst ein Kommentar: Diese Art von Ausdruck wird in keiner (vernünftigen) Produktionsumgebung auftauchen und ist nur als Übung dafür nützlich, wie gut der Leser die schmutzigen Kanten von JavaScript kennt. Das allgemeine Prinzip, dass JavaScript-Operatoren implizit zwischen Typen konvertieren, ist nützlich, ebenso wie einige der gängigen Konvertierungen, viele Details in diesem Fall jedoch nicht.
Der Ausdruck
++[[]][+[]]+[+[]]
mag anfangs eher imposant und undurchsichtig aussehen, ist aber tatsächlich relativ einfach in separate Ausdrücke zu zerlegen. Im Folgenden habe ich aus Gründen der Übersichtlichkeit einfach Klammern hinzugefügt. Ich kann Ihnen versichern, dass sie nichts ändern, aber wenn Sie dies überprüfen möchten, können Sie sich über den Gruppierungsoperator informieren . Der Ausdruck kann also klarer geschrieben werden alsWenn wir dies aufschlüsseln, können wir es vereinfachen, indem wir beobachten, dass es
+[]
ausgewertet wird0
. Um sich davon zu überzeugen , warum dies der Fall ist, überprüfen Sie den Operator unary + und folgen Sie dem leicht gewundenen Pfad, der dazu führt, dass ToPrimitive das leere Array in eine leere Zeichenfolge konvertiert, die dann0
von ToNumber schließlich konvertiert wird . Wir können jetzt0
jede Instanz von ersetzen+[]
:Schon einfacher. Dies
++[[]][0]
ist eine Kombination aus dem Präfix-Inkrement-Operator (++
), einem Array-Literal , das ein Array mit einem einzelnen Element definiert, das selbst ein leeres Array ([[]]
) ist, und einem Eigenschafts-Accessor ([0]
), der für das durch das Array-Literal definierte Array aufgerufen wird.Also können wir
[[]][0]
es einfach vereinfachen[]
und wir haben es++[]
, richtig? In der Tat ist dies nicht der Fall, da die Auswertung++[]
einen Fehler auslöst, der zunächst verwirrend erscheinen kann. Ein kleiner Gedanke über die Art von++
macht dies jedoch deutlich: Es wird verwendet, um eine Variable (z. B.++i
) oder eine Objekteigenschaft (z++obj.count
. B. ) zu erhöhen . Es wird nicht nur zu einem Wert ausgewertet, sondern dieser Wert wird auch irgendwo gespeichert. Im Fall von++[]
kann der neue Wert nirgendwo platziert werden (was auch immer er sein mag), da kein Verweis auf eine zu aktualisierende Objekteigenschaft oder Variable vorhanden ist. In technischen Begriffen wird dies durch die interne PutValue- Operation abgedeckt , die vom Präfix-Inkrement-Operator aufgerufen wird.Also, was macht
++[[]][0]
dann? Nun, nach ähnlicher Logik wie+[]
wird das innere Array konvertiert0
und dieser Wert wird um erhöht1
, um einen endgültigen Wert von zu erhalten1
. Der Wert der Eigenschaft0
im äußeren Array wird aktualisiert1
und der gesamte Ausdruck wird ausgewertet1
.Das lässt uns mit
... was eine einfache Verwendung des Additionsoperators ist . Beide Operanden werden zuerst in Grundelemente konvertiert. Wenn einer der Grundelemente eine Zeichenfolge ist, wird eine Zeichenfolgenverkettung durchgeführt, andernfalls wird eine numerische Addition durchgeführt.
[0]
konvertiert nach"0"
, so dass die Verkettung von Zeichenfolgen verwendet wird, wodurch erzeugt wird"10"
.Abgesehen davon ist etwas, das möglicherweise nicht sofort ersichtlich ist, dass das Überschreiben einer
toString()
odervalueOf()
mehrerer MethodenArray.prototype
das Ergebnis des Ausdrucks ändert, da beide überprüft und verwendet werden, wenn sie vorhanden sind, wenn ein Objekt in einen primitiven Wert konvertiert wird. Zum Beispiel das Folgende... produziert
"NaNfoo"
. Warum dies geschieht, bleibt dem Leser als Übung überlassen ...quelle
Machen wir es einfach:
quelle
Dieser wird gleich bewertet, aber etwas kleiner
so wird ausgewertet
Also jetzt haben Sie das, versuchen Sie dieses:
quelle
"10"
+ [] wird zu 0 [...] ausgewertet und dann durch Summieren (+ Operation) mit irgendetwas konvertiert der Array-Inhalt in seine Zeichenfolgendarstellung, die aus mit Komma verbundenen Elementen besteht.
Alles andere wie das Aufnehmen des Array-Index (mit größerer Priorität als + Operation) ist ordinal und nichts Interessantes.
quelle
Möglicherweise sind die kürzesten Möglichkeiten, einen Ausdruck ohne Ziffern in "10" zu bewerten, folgende:
+!+[] + [+[]]
// "10"-~[] + [+[]]
// "10"// ========== Erklärung ========== \\
+!+[]
:+[]
Konvertiert in 0.!0
Konvertiert intrue
.+true
konvertiert zu 1.-~[]
=-(-1)
was 1 ist[+[]]
:+[]
Konvertiert in 0.[0]
ist ein Array mit einem einzelnen Element 0.Dann JS wertet den
1 + [0]
damitNumber + Array
Ausdruck. Dann funktioniert die ECMA-Spezifikation: Der+
Operator konvertiert beide Operanden in einen String, indem er dietoString()/valueOf()
Funktionen von der Basis aus aufruftObject
. Es funktioniert als additive Funktion, wenn beide Operanden eines Ausdrucks nur Zahlen sind. Der Trick besteht darin, dass Arrays ihre Elemente leicht in eine verkettete Zeichenfolgendarstellung konvertieren.Einige Beispiele:
Es gibt eine schöne Ausnahme, zu der zwei
Objects
Additionen führenNaN
:quelle
+ '' oder + [] ergibt 0.
quelle
[]
ist nicht gleichbedeutend mit""
. Zuerst wird das Element extrahiert und dann von konvertiert++
.Verwandeln Sie den
+
Wert Schritt für Schritt in eine Zahl, und wenn Sie ein leeres Array hinzufügen+[]
... da es leer ist und gleich ist,0
wird esSchauen Sie sich von dort aus Ihren Code an, es ist
++[[]][+[]]+[+[]]
...Und es gibt und zwischen ihnen
++[[]][+[]]
+[+[]]
Diese
[+[]]
kehren also zurück,[0]
da sie ein leeres Array haben, das0
in das andere Array konvertiert wird ...So wie ich vorstellen, der erste Wert ist ein 2-dimensionales Array mit einem Array innerhalb ... so
[[]][+[]]
gleich sein wird,[[]][0]
die die Rückkehr wird[]
...Und am Ende
++
konvertieren Sie es und erhöhen Sie es auf1
...Sie können sich also vorstellen,
1
+"0"
wird"10"
...quelle