Frage Warum gibt ++ [[]] [+ []] + [+ []] die Zeichenfolge "10" zurück?


Dies ist gültig und gibt die Zeichenfolge zurück "10" in JavaScript (mehr Beispiele hier):

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

Warum? Was passiert hier?


1439
2017-08-26 08:56


Ursprung


Antworten:


Wenn wir es aufteilen, ist das Chaos gleich:

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

In JavaScript stimmt das +[] === 0. + wandelt etwas in eine Zahl um, und in diesem Fall wird es darauf ankommen +"" oder 0 (Siehe Spezifikationsdetails unten).

Daher können wir es vereinfachen (++ hat Vorrang vor +):

++[[]][0]
+
[0]

weil [[]][0] bedeutet: Holen Sie sich das erste Element aus [[]], es stimmt, dass:

  • [[]][0] gibt das innere Array zurück ([]). Aufgrund von Referenzen ist es falsch zu sagen [[]][0] === [], aber nennen wir das innere Array A um die falsche Schreibweise zu vermeiden.
  • ++[[]][0] == A + 1, schon seit ++ bedeutet "um Eins erhöhen".
  • ++[[]][0] === +(A + 1); Mit anderen Worten, es wird immer eine Nummer sein (+1 gibt nicht unbedingt eine Zahl zurück, wohingegen ++ tut es immer - dank Tim Down, dass er darauf hingewiesen hat.

Wiederum können wir das Chaos in etwas besser lesbares vereinfachen. Lass uns ersetzen [] zurück für A:

+([] + 1)
+
[0]

In JavaScript gilt das auch: [] + 1 === "1", weil [] == "" (Beitritt zu einem leeren Array), also:

  • +([] + 1) === +("" + 1), und
  • +("" + 1) === +("1"), und
  • +("1") === 1

Lass es uns noch einfacher machen:

1
+
[0]

Dies gilt auch für JavaScript: [0] == "0", weil es ein Array mit einem Element verbindet. Joining verkettet die Elemente, getrennt durch ,. Mit einem Element können Sie folgern, dass diese Logik zum ersten Element selbst führt.

Also, am Ende erhalten wir (Zahl + String = String):

1
+
"0"

=== "10" // Yay!

Spezifikationsdetails für +[]:

Das ist ein Labyrinth, aber zu tun +[]Zuerst wird es in eine Zeichenkette umgewandelt, weil es das ist + sagt:

11.4.6 Unärer + Operator

Der unary + -Operator konvertiert seinen Operanden in den Zahlentyp.

Die Produktion UnaryExpression: + UnaryExpression wird wie folgt ausgewertet:

  1. Sei expr das Ergebnis der Evaluierung von UnaryExpression.

  2. Return ToNumber (GetValue (Ausdruck)).

ToNumber() sagt:

Objekt

Übernehmen Sie die folgenden Schritte:

  1. Sei primValue ToPrimitive (Eingabeargument, Hinweiszeichenfolge).

  2. Zurück zu String (primValue).

ToPrimitive() sagt:

Objekt

Gibt einen Standardwert für das Objekt zurück. Der Standardwert eines Objekts wird abgerufen, indem die interne Methode [[DefaultValue]] des Objekts aufgerufen und der optionale Hinweis PreferredType übergeben wird. Das Verhalten der internen Methode [[DefaultValue]] ist in dieser Spezifikation für alle nativen ECMAScript-Objekte in 8.12.8 definiert.

[[DefaultValue]] sagt:

8.12.8 [[DefaultValue]] (Hinweis)

Wenn die interne Methode [[DefaultValue]] von O mit dem Hinweis String aufgerufen wird, werden die folgenden Schritte ausgeführt:

  1. Sei toString das Ergebnis des Aufrufs der internen Methode [[Get]] des Objekts O mit dem Argument "toString".

  2. Wenn IsCallable (toString) wahr ist,

ein. Sei str das Ergebnis des Aufrufs der internen [[Call]] - Methode von toString, wobei O der this-Wert und eine leere Argumentliste ist.

b. Wenn str ein primitiver Wert ist, gebe str zurück.

Das .toString eines Arrays sagt:

15.4.4.2 Array.prototype.toString ()

Wenn die toString-Methode aufgerufen wird, werden die folgenden Schritte ausgeführt:

  1. Lassen Sie Array das Ergebnis des Aufrufs von ToObject auf diesem Wert sein.

  2. Lassen Sie Func das Ergebnis des Aufrufs der internen Methode [[Get]] von Array mit Argument "Join" sein.

  3. Wenn IsCallable (func) false ist, sollte func die integrierte Standardmethode Object.prototype.toString (15.2.4.2) sein.

  4. Gibt das Ergebnis des Aufrufs der internen [[Call]] - Methode von func zurück, wobei array als dieser Wert und eine Liste leerer Argumente bereitgestellt wird.

Damit +[] kommt auf +"", weil [].join() === "".

Wiederum, die + ist definiert als:

11.4.6 Unärer + Operator

Der unary + -Operator konvertiert seinen Operanden in den Zahlentyp.

Die Produktion UnaryExpression: + UnaryExpression wird wie folgt ausgewertet:

  1. Sei expr das Ergebnis der Evaluierung von UnaryExpression.

  2. Return ToNumber (GetValue (Ausdruck)).

ToNumber ist definiert für "" wie:

Der MV von StringNumericLiteral ::: [leer] ist 0.

Damit +"" === 0, und somit +[] === 0.


1874
2017-08-26 08:58



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

Dann haben wir eine String-Verkettung

1+[0].toString() = 10

99
2017-09-14 13:54



Folgendes wird von a angepasst Blogeintrag Beantwortung dieser Frage, die ich gepostet habe, während diese Frage noch geschlossen war. Links sind (eine HTML-Kopie von) der ECMAScript 3-Spezifikation, immer noch die Basis für JavaScript in den heute üblichen Webbrowsern.

Erstens, ein Kommentar: Diese Art von Ausdruck wird niemals in einer (vernünftigen) Produktionsumgebung auftauchen und nur als Übung dafür dienen, 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 üblichen Konvertierungen, aber ein großer Teil des Details ist in diesem Fall nicht möglich.

Der Ausdruck ++[[]][+[]]+[+[]] sieht zunächst eher imposant und undurchsichtig aus, ist aber relativ leicht in einzelne Ausdrücke zerlegbar. Im Folgenden habe ich einfach die Klammern zur besseren Übersicht hinzugefügt. Ich kann Ihnen versichern, dass sie nichts ändern, aber wenn Sie das überprüfen möchten, dann fühlen Sie sich frei, über das zu lesen Gruppierungsoperator. So kann der Ausdruck klarer als geschrieben werden

( ++[[]][+[]] ) + ( [+[]] )

Wenn wir das durchbrechen, können wir das vereinfachen, indem wir das beobachten +[] bewertet zu 0. Um sich selbst zu überzeugen, warum dies der Fall ist, sehen Sie sich die unärer + Operator und folgen Sie der leicht gewundenen Spur, die mit endet ToPrimitiv Konvertieren des leeren Arrays in eine leere Zeichenfolge, die schließlich in konvertiert wird 0 durch Umnummer. Wir können jetzt ersetzen 0 für jede Instanz von +[]:

( ++[[]][0] ) + [0]

Einfacher schon. Wie für ++[[]][0], das ist eine Kombination aus der Präfix-Inkrementoperator (++), ein Array-Literal Definieren eines Arrays mit einem einzelnen Element, das selbst ein leeres Array ist ([[]]) und ein Eigenschaft Accessor ([0]) hat das vom Array-Literal definierte Array aufgerufen.

Also können wir vereinfachen [[]][0] zu einfach [] und wir haben ++[], Recht? In der Tat ist dies nicht der Fall, weil die Bewertung ++[] löst einen Fehler aus, der zunächst verwirrend erscheinen mag. Allerdings ein wenig über die Natur der ++ macht dies klar: Es wird verwendet, um eine Variable (z.B. ++i) oder eine Objekteigenschaft (z.B. ++obj.count). Sie wertet nicht nur einen Wert aus, sondern speichert diesen Wert auch irgendwo. Im Falle des ++[]Es kann nirgendwo der neue Wert eingegeben werden (was auch immer es sein mag), weil es keinen Verweis auf eine Objekteigenschaft oder eine zu aktualisierende Variable gibt. Genau genommen wird dies durch das interne abgedeckt PutValue Operation, die vom Präfix Inkrementoperator aufgerufen wird.

Also, was macht ++[[]][0] machen? Nun, durch ähnliche Logik wie +[], das innere Array wird in konvertiert 0 und dieser Wert wird um erhöht 1 um uns einen Endwert von 1. Der Wert von Eigentum 0 im äußeren Array wird aktualisiert auf 1 und der ganze Ausdruck wird ausgewertet 1.

Das lässt uns mit

1 + [0]

... das ist eine einfache Verwendung der Additionsoperator. Beide Operanden sind erste in Primitive konvertiert und wenn jeder Grundwert eine Zeichenkette ist, wird eine Zeichenkettenverkettung durchgeführt, andernfalls wird eine numerische Addition durchgeführt. [0] konvertiert zu "0", so wird String-Verkettung verwendet, produziert "10".

Als letztes ist etwas, das vielleicht nicht sofort offensichtlich ist, das Überschreiben eines der beiden toString() oder valueOf() Methoden von Array.prototype wird das Ergebnis des Ausdrucks ändern, da beide überprüft und verwendet werden, wenn sie beim Konvertieren eines Objekts in einen Grundwert vorhanden sind. Zum Beispiel das Folgende

Array.prototype.toString = function() {
  return "foo";
};
++[[]][+[]]+[+[]]

... produziert "NaNfoo". Warum das passiert, bleibt als Übung für den Leser ...


57
2017-12-30 15:41



Lass es uns einfach machen:

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

21
2017-12-28 23:13



Dieser bewertet dasselbe, aber ein bisschen kleiner

+!![]+''+(+[])
  • [] - wird ein Array konvertiert, das beim Hinzufügen oder Subtrahieren in 0 konvertiert wird, also also + [] = 0
  • ! [] - ergibt false, daher wird !! [] als wahr ausgewertet
  • + !! [] - konvertiert den Wert true in einen numerischen Wert, der als true ausgewertet wird, also in diesem Fall 1
  • + '' - Fügt dem Ausdruck eine leere Zeichenfolge hinzu, die bewirkt, dass die Zahl in eine Zeichenfolge konvertiert wird
  • + [] - ergibt 0

so wird bewertet

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

So, jetzt hast du das, probier dieses hier aus:

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

13
2017-08-26 08:58



+ [] ergibt 0 [...] dann Summierung (+ Operation) es mit allem konvertiert Array-Inhalt in seine String-Darstellung, bestehend aus Elementen mit Komma verbunden.

Alles andere, wie den Array-Index zu nehmen (hat eine höhere Priorität als + Operation), ist ordinal und ist nichts Interessantes.


7
2017-12-30 08:10



Vielleicht sind die kürzesten Wege, um einen Ausdruck in "10" ohne Ziffern auszuwerten:

+!+[] + [+[]] // "10"

-~[] + [+[]]  // "10"

// ========== Erklärung ========== \\

+!+[] : +[] Konvertiert in 0. !0 konvertiert zu true. +true konvertiert zu 1. -~[] = -(-1) was 1 ist

[+[]] : +[] Konvertiert in 0. [0] ist ein Array mit einem einzelnen Element 0.

Dann wertet JS die 1 + [0]so Number + Array Ausdruck. Dann funktioniert die ECMA-Spezifikation: + operator konvertiert beide Operanden in eine Zeichenkette durch Aufruf der toString()/valueOf() funktioniert von der Basis aus Object Prototyp. Sie arbeitet als additive Funktion, wenn beide Operanden eines Ausdrucks nur Zahlen sind. Der Trick besteht darin, dass Arrays ihre Elemente einfach in eine verkettete Zeichenfolgendarstellung konvertieren.

Einige Beispiele:

1 + {} //    "1[object Object]"
1 + [] //    "1"
1 + new Date() //    "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"

Es gibt eine nette Ausnahme, dass zwei Objects Addition ergibt NaN:

[] + []   //    ""
[1] + [2] //    "12"
{} + {}   //    NaN
{a:1} + {b:2}     //    NaN
[1, {}] + [2, {}] //    "1,[object Object]2,[object Object]"

4



  1. Unary plus angegebene Zeichenfolge wird in Zahl konvertiert
  2. Increment Operator angegebene Zeichenfolge konvertiert und erhöht sich um 1
  3. [] == ''. Leerer String
  4. + '' oder + [] wertet 0 aus.

    ++[[]][+[]]+[+[]] = 10 
    ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 
    1+0 
    10
    

1