Warum wird! {} [True] in JavaScript als true ausgewertet?

131

{}[true]ist [true]und ![true]soll sein false.

Warum also !{}[true]bewerten true?

user2430508
quelle
30
var o = {}; o[true] === undefined.
Azz
2
Die Erklärung hier wird wahrscheinlich den in dieser vorherigen Frage
IMSoP
45
"Weil Javascript albern ist" ist wahrscheinlich nicht die Antwort, nach der Sie suchen.
Georg
2
Wie bereits erwähnt, wird {}[true] === [true]eine Konsole {}als leerer Codeblock und nicht als Objekt behandelt.
Azz
3
Wenn es helfen kann, versuchen Sie, {}und ({})in Ihrer Konsole (oder {}[true]und ({})[true]) zu vergleichen . Wie niemand es erwähnt hat, wird das Objekt [wahr] als Objekt ["wahr"] ausgewertet.
BiAiB

Antworten:

172

Ich glaube, das {}[true]liegt daran, dass plain als leerer Anweisungsblock (kein Objektliteral) analysiert wird, gefolgt von einem Array, das enthälttrue , dastrue :

Wenn Sie jedoch den !Operator anwenden , wird der Parser {}als Objektliteral interpretiert , sodass das Folgende {}[true]zu einem Elementzugriff wird, der zurückkehrt undefinedund !{}[true]tatsächlich true(wie er !undefinedist true) ist.

Frédéric Hamidi
quelle
25
Die Tatsache, dass! Undefined wahr ist, ist andererseits immer noch unentschuldbar.
Evilcandybag
87
@evilcandybag: Es ist absolut nicht. undefinedist falsch (etwas, auf das wir uns oft verlassen - if (obj.maybeExists) ...), also macht es vollkommen logisch Sinn, dass !undefinedes wahr ist.
Josh3736
8
@ Josh, ich denke, evilcandybag würde ein Verhalten bevorzugen, das nullin einigen Sprachen ähnlich !undefinedist und gleich ist undefined. Dies ist in Javascript jedoch nicht der Fall.
Frédéric Hamidi
6
@evilcandybag: Es ist nur logisch zu sagen, dass etwas, das not undefined( !undefined) ist, daher definiert werden muss. Wenn etwas definiert ist, wird es normalerweise als interpretiert true.
OozeMeister
7
@Cruncher Wenn a undefiniert und b undefiniert ist, wie können wir dann möglicherweise wissen, dass a! = B? Insbesondere wenn die einzige bekannte Eigenschaft der beiden Variablen genau gleich ist.
LJ2
44

Weil {}[true]nicht zurückgibt true, sondern undefinedund undefinedausgewertet wird als false:

http://jsfiddle.net/67GEu/

'use strict';
var b = {}[true];
alert(b); // undefined
b = !{}[true];
alert(b); // true
dooxe
quelle
21
Wenn Sie {}[true]in einer Konsole auswerten , erhalten Sie [true], weil der {}als leerer Codeblock interpretiert wird, nicht als Objekt. Es geht um den Kontext und die Mehrdeutigkeit von {}.
IMSoP
1
@IMSoP aber warum wird {key:"value"}[1,2,3];auch ausgewertet [1,2,3]?
t.niese
3
@ t.niese, da es als Anweisungsblock analysiert wird, der ein label ( key:) und ein Zeichenfolgenliteral ( "value") enthält, gefolgt von einem Array. Der Parser sieht immer noch kein Objektliteral.
Frédéric Hamidi
1
@ FrédéricHamidi ah ja, das ist es. Ich habe Etiketten unterdrückt ^^
t.niese
1
@dooxe Lesen Sie die anderen Antworten; Es geht um den Kontext, in dem es interpretiert wird. Wenn Sie es in alert()oder umschließen oder console.log()einer Variablen zuweisen, ändern Sie den Kontext, weshalb es sich nicht so verhält, wie es allein in einer Konsole eingegeben wurde.
IMSoP
27

weil

{}[true]

bewertet zu undefinedund !undefinedisttrue .

Von @schlingel:

truewird als Schlüssel und {}als Hash-Map verwendet. Es gibt keine Eigenschaft mit dem Schlüssel, truedaher wird dieser zurückgegeben undefined. Nicht undefinedisttrue , wie erwartet.

Konsolensitzung ( Node.js [0.10.17] ):

> {}[true]
undefined
> !{}[true]
true
> [true]
[ true ]
> ![true]
false
>

In der Google Chrome- Konsole:

> !{}[true]
true

Also keine Inkonsistenzen. Sie verwenden wahrscheinlich eine alte Version der JavaScript-VM. Für diejenigen, die weitere Beweise benötigen:

Geben Sie hier die Bildbeschreibung ein

AKTUALISIEREN

Mit Firefox wird außerdem Folgendes ausgewertet true:

Geben Sie hier die Bildbeschreibung ein

Spiele Brainiac
quelle
Nicht, wenn Sie es tun eval('{}[true]')oder in die Konsole eingeben. Dann zB als {}"test"ist testoder sogar {key:"value"}"test"ist test.
t.niese
Interessant, in welcher js Engine testest du das?
t.niese
@ t.niese Ich habe es gerade in meine Knotenkonsole eingegeben, und das habe ich bekommen.
Spiele Brainiac
Nur aus Neugier. Kommt {}[true];(mit dem ;) [true]für dich zurück, weil es hier tut?
t.niese
2
Grund für Downvote Jungs? Es gibt eine fast identische Antwort auf diese mit 8 Stimmen, und ich bekomme die Abwertung? Was habe ich falsch gemacht?
Spiele Brainiac
23

Der Grund für die Verwirrung liegt in einem Missverständnis Ihrer ersten Behauptung:

{}[true] ist [true]

Was Sie beim Ausführen sehen, ist das Ergebnis einer Mehrdeutigkeit. Javascript verfügt über definierte Regeln für den Umgang mit solchen Mehrdeutigkeiten. In diesem Fall wird das, was Sie als Signle-Anweisung betrachten, in zwei separate Anweisungen unterteilt.

Javascript sieht den obigen Code also als zwei separate Anweisungen: Erstens gibt es eine {}und dann eine völlig separate [true]. Die zweite Aussage gibt Ihnen das Ergebnis [true]. Die erste Aussage {}wird praktisch vollständig ignoriert.

Sie können dies beweisen, indem Sie Folgendes versuchen:

({}[true])

dh das Ganze in Klammern setzen, um den Interpreter zu zwingen, es als einzelne Anweisung zu lesen.

Jetzt sehen Sie, dass der tatsächliche Wert Ihrer Anweisung ist undefined . (Dies wird uns später auch helfen, den nächsten Teil zu verstehen.)

Jetzt wissen wir, dass der erste Teil Ihrer Frage ein roter Hering ist. Gehen wir also zum letzten Teil der Frage über:

Warum wird! {} [True] als true ausgewertet?

Hier haben wir die gleiche Aussage, aber mit einem !an die Vorderseite angehängten.

In diesem Fall schreiben die Regeln von Javascript vor, dass das Ganze als einzelne Anweisung bewertet werden soll.

Verweisen Sie auf das, was passiert ist, als wir die frühere Aussage in Klammern gesetzt haben. wir haben undefined. Dieses Mal machen wir effektiv das Gleiche, stellen aber ein !davor. So kann Ihr Code wie !undefinedfolgt vereinfacht werden true.

Hoffentlich erklärt das ein bisschen.

Es ist ein komplexes Tier, aber die Lektion, die Sie hier lernen müssen, besteht darin, bei der Auswertung in der Konsole Klammern um Ihre Aussagen zu verwenden, um solche falschen Ergebnisse zu vermeiden.

Spudley
quelle
2
Ich denke nicht, dass {}[true]es genau ungültig ist , nur mehrdeutig . Es kann entweder als "leerer Codeblock gefolgt von einem Array-Literal" oder als "Objektliteral ohne Eigenschaften, auf die auf eine Eigenschaft zugegriffen wird" interpretiert werden. Ich weiß nicht, ob es sich bei der ersten technisch um ASI handelt (viele Sprachen würden dort sowieso kein Semikolon einfügen), aber es ist die kontextsensitive Interpretation, die das Herzstück des Problems darstellt.
IMSoP
@IMSoP - Ich habe die Antwort bereits bearbeitet, bevor Sie den Kommentar gepostet haben. :)
Spudley
1
Gleich zu Beginn der Antwort steht immer noch "{} [true] ist überhaupt nicht gültig".
IMSoP
Außerdem hat OP nicht " {}[true]ist true" gesagt, sondern " {}[true]ist [true]", was eine der beiden gültigen Interpretationen der mehrdeutigen Aussage ist.
IMSoP
14

{}[true]ist undefined. Um das zu finden, schreiben Sie Folgendes:

a = {};
a[true] === undefined // true

oder einfach:

({})[true] === undefined // true

Wir wissen , dass !undefinedist true.


Aus der Antwort von @Benjamin Gruenbaum :

Chrome Dveloper Tools bietet Folgendes :

  try {
      if (injectCommandLineAPI && inspectedWindow.console) {
          inspectedWindow.console._commandLineAPI = new CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
          expression = "with ((window && window.console && window.console._commandLineAPI) || {}) {\n" + expression + "\n}";
      }
      var result = evalFunction.call(object, expression);
      if (objectGroup === "console")
          this._lastResult = result;
      return result;
  } 
  finally {
      if (injectCommandLineAPI && inspectedWindow.console)
          delete inspectedWindow.console._commandLineAPI;
  }

Im Grunde führt es ein callObjekt mit dem Ausdruck aus. Der Ausdruck ist:

with ((window && window.console && window.console._commandLineAPI) || {}) {
    {}+{};// <-- This is your code
}

Wie Sie sehen, wird der Ausdruck direkt ohne die umschließende Klammer ausgewertet.

Weitere Informationen finden Sie in dieser Frage .

Ionică Bizău
quelle
10

Die Antworten hier sind gut, hier ist eine Aufschlüsselung des Pseudocodes:

  • {}['whatever'] = leerer Block, NewArray ('was auch immer') = NewArray ('was auch immer')
  • {}[true] = leerer Block, NewArray (true) = NewArray (true)
  • !{}['whatever'] = LogicalNOT (convertToBool (NewObject.whatever)) = LogicalNOT (convertToBool (undefiniert)) = LogicalNOT (false) = true
  • ({}['whatever']) = Gruppierung (NewObject.whatever) = Gruppierung (undefiniert) = undefiniert
Ben Lesh
quelle
8

Dies geschieht, weil {}in Ihrer Bedeutung nicht wörtliche Darstellung Object, sondern leerer Bereich (oder leerer Codeblock) ist:

{ var a = 1 }[true] // [true] (do the same thing)

Es wertet nur den Code innerhalb des Gültigkeitsbereichs aus und zeigt Ihnen dann Ihr Array.

Und von deinem

!{}[true]

Konvertiert einfach in diesen Bereich und gibt dasselbe Array true zurück. In diesem Code gibt es keine Bool-Prüfungen.

Und wenn Sie versuchen, das Ergebnis zu überprüfen, erhalten {}[true]Sie Folgendes false:

{}[true] -> [true] -> ![true] -> false

Da gibt es keinen Spielraum mehr.

Machen Sie !in Ihrer Frage dasselbe wie:

!function() {
   //...
}
Antyrat
quelle
Dies ist leichter zu erkennen, wenn Sie dies tun var x = {}; x[true].
Chris Hayes
1
Ich bin mir nicht sicher, was Sie unter "Konvertieren in diesen Bereich" verstehen. Ich denke , mit dem führenden !es wird als ein leeres Objekt interpretiert, nicht Umfang, und dies ist die Diskrepanz.
IMSoP
6
  • {} ist ein Objekt ohne Eigenschaften.
  • Da es []unmittelbar auf ein Objekt folgt, bedeutet dies "Zugriff auf eine Eigenschaft dieses Namens" und nicht "Array erstellen".
  • trueist ein Boolescher Wert, wird jedoch als Eigenschaftsname verwendet, sodass er in einen String umgewandelt wird ( "true")
  • Das Objekt verfügt nicht über eine Eigenschaft namens true(da es keine Eigenschaften hat) , so {}['true']istundefined
  • !undefinedCasts undefinedzu einem Booleschen Wert ( false)
  • Der Nicht-Operator wird falsezu true.
QUentin
quelle
2
Im Fall von {}[true](ohne anderen Kontext) {}handelt es sich nicht um ein Objekt ohne Eigenschaften, sondern um einen leeren Codeblock.
IMSoP
4

Lass uns ein bisschen mehr spielen!

Lass uns zuerst ein bisschen Spaß haben!:

//----------#01#-----------
{}[true]; //[true]

//----------#02#-----------
var a = {}[true]; 
      console.log(a); //undefined

//----------#03#-----------
{ b: 12345 }[true]; //[true]

//----------#04#-----------
{ b: 12345 }["b"]; //evaluates to ["b"] ?!?

//----------#05#-----------
{ b: 12345 }.b; // "Unexpected token ."

//----------#06#-----------
({ b: 12345 }).b; //12345

//----------#07#-----------
var c = { b: 12345 }.b; 
      console.log(c); //12345

//----------#08#-----------
var c = { b: 12345 }["b"];
      console.log(c); //12345

//----------#09#-----------
{ true: 54321 }[true]; // "SyntaxError: Unexpected token : "

//----------#10#-----------
var d = { true: 54321 }[true]; //No error here ¬¬
      console.log(d); //54321

//----------#11#-----------
!{}[true]; // true

Ok, lass uns versuchen, diese verrückten Verhaltensweisen einzeln zu verstehen:

1) Hier wird das {}als leerer Codeblock analysiert. Ohne Zuweisung, Negation, Gruppierung (mit Klammern) oder einer Syntax, die dem Parser anzeigt, dass dies {}ein Objektliteral ist, wird standardmäßig angenommen, dass es sich einfach um einen nutzlosen leeren Block handelt.

Dies ist ein Beweis für dieses Verhalten:

{ alert(123) }[true]

Der obige Code zeigt die Warnung normalerweise, und wie bewertet wird [true], in der gleichen Art und Weise {}[true]ist.

Blockanweisungen ohne Semikolons

Eine blockartige Anweisung benötigt danach kein Semikolon.

Zum Beispiel:

for(var i=0; i < 1; i++){}function a(){};alert("Passed here!");if(true){}alert("Passed here too!")

Beide Warnungen werden angezeigt.

Wir können also sehen, dass eine leere Blockanweisung ohne Semikolon gültig ist und einfach nichts bewirkt. Auf diese Weise ist {}[true]der ausgewertete Wert beim Aufrufen der Developer Tools (oder Firebug) -Konsole der Wert der letzten Ausdrucksanweisung . In diesem Fall lautet die letzte Ausdrucksanweisung[true] .

2) In einem Zuweisungskontext stellt der Parser sicher, dass {}es sich um ein Objektliteral handelt. Wenn Sie var a = {}[true]ausführen, entfernen Sie alle Mehrdeutigkeiten und geben dem Parser {}einen Hinweis , der keine Blockanweisung ist.
Hier versuchen Sie also, mit einem Schlüssel einen Wert zu erhalten"true" von einem leeren Objekt abzurufen. Offensichtlich gibt es kein Schlüssel-Wert-Paar mit diesem Schlüsselnamen. Auf diese Weise ist die Variable a undefiniert.

Reservierte Wörter als Objektschlüssel

Mit ECMAScript 5 können Objektschlüssel reservierte Wörter sein. Die folgenden Schlüssel sind also legal:

var obj = {if: 111, for: 222, switch: 333, function: 444, true: 555}

3) Die gleiche Erklärung von Beispiel 1 . Aber ... Wenn das { b: 12345 }Teil als Blockanweisung behandelt wird, wie lautet die Art der b: 12345Anweisung?

... (?????)

Es ist eine Label-Anweisung , die Sie bereits gesehen haben ... Sie wird in Loops und in verwendet switch. Hier sind einige interessante Links zu Label-Anweisungen: 1 , (2) [Der beste Weg, um aus verschachtelten Schleifen in Javascript auszubrechen? , (3) [ Wie werden verschachtelte Schleifen in Javascript unterbrochen? .

HINWEIS: Versuchen Sie einfach, dies zu bewerten:

{a: 1, b: 2} //=>>>SyntaxError: Unexpected token :

Label-Anweisungen können vom Komma- Operator nicht getrennt werden. Sie müssen sie mit einem Semikolon trennen. Das ist also gültig:{a: 1; b: 2}

4) Siehe die Erläuterungen zu den Beispielen 1 und 3 ...

5) Noch einmal, wir werden { b: 12345 }als Codeblock behandelt, und Sie versuchen, mithilfe der Punktnotation auf eine Eigenschaft eines Codeblocks zuzugreifen. Dies ist natürlich nicht zulässig, und der Parser löst eine "Unexpected token :"Ausnahme aus.

6) Der Code ist fast identisch mit dem obigen Beispiel, aber durch Umgeben der { b: 12345 }Anweisung mit dem Ausdrucksgruppierungsoperator weiß der Parser, dass es sich um ein Objekt handelt. Auf diese Weise können Sie "b"normal auf die Unterkunft zugreifen .

7) Denken Sie an Beispiel 2 , wir haben hier eine Zuordnung, der Parser weiß, dass { b: 12345 }es sich um ein Objekt handelt.

8) Identisch mit dem obigen Beispiel, aber anstelle der Punktnotation verwenden wir hier die Klammernotation .

9) Ich habe bereits gesagt, dass diese "identifier: value"Syntax in einer Blockanweisung eine Bezeichnung ist. Sie müssen jedoch auch wissen, dass ein Labelname kein reserviertes Schlüsselwort sein kann (das Gegenteil von Objekteigenschaftsnamen). Als wir versuchten, ein Label namens zu definieren "true", bekamen wir ein SyntaxError.

10) Wir haben es wieder mit einem Objekt zu tun. Keine Probleme mit reservierten Wörtern hier. =)

11) Schließlich haben wir Folgendes:!{}[true]

Lassen Sie uns die Dinge hier trennen:

a) Durch eine Negation informieren wir den Parser, dass {}es sich um ein Objekt handelt .

b) Wie in Beispiel 2 gezeigt , hat ein {}Objekt keine aufgerufene Eigenschaft true, daher wird dieser Ausdruck als ausgewertet undefined.

c) Das Endergebnis ist die Negation des undefinedWertes. Javascript führt eine Konvertierung des Implizitätstyps durch , und der undefinedWert ist falsch .

d) Die Negation von falseist also ... true!

Alcides Queiroz Aguiar
quelle