Perché! {} [True] valuta true in JavaScript?


131

{}[true]è [true]e ![true]dovrebbe essere false.

Quindi perché !{}[true]valuta true?


30
var o = {}; o[true] === undefined.
azz

2
La spiegazione qui sarà probabilmente molto simile alle stranezze discusse su questa domanda precedente
IMSoP

45
"Perché Javascript è stupido" non è probabilmente la risposta che stai cercando.
Georg

2
Come accennato, se stai ricevendo {}[true] === [true]da una console, è perché si tratta {}di un blocco di codice vuoto, non di un oggetto.
azz

3
se può aiutare, prova a confrontare {}e ({})nella tua console (o {}[true]e ({})[true]). Inoltre, come nessuno lo ha menzionato, l'oggetto [vero] viene valutato come oggetto ["vero"].
BiAiB

Risposte:


172

Credo sia perché il semplice {}[true]viene analizzato come un blocco di istruzioni vuoto (non un oggetto letterale) seguito da una matrice contenente true, che ètrue .

D'altra parte, applicando l' !operatore esegue il parser interpreta {}come un letterale oggetto, quindi il seguente {}[true]diventa un accesso membro che ritorni undefined, ed !{}[true]è in effetti true(come !undefinedè true).


25
Il fatto che! Indefinito sia vero, d'altra parte, è ancora imperdonabile.
evilcandybag,

87
@evilcandybag: assolutamente no. undefinedè falsa (qualcosa su cui ci affidiamo spesso - if (obj.maybeExists) ...), quindi ha un perfetto senso logico che !undefinedè vero.
josh3736,

8
@Josh, penso che evilcandybag preferirebbe un comportamento simile nullin alcune lingue, con l' !undefinedessere uguale a undefined. Questo non è il caso di Javascript, però.
Frédéric Hamidi,

6
@evilcandybag: ha senso logicamente solo dire che qualcosa che è not undefined( !undefined) deve quindi essere definito. Se qualcosa è definito, di solito viene interpretato come true.
OozeMeister,

7
@Cruncher Se a non è definito e b non è definito, come possiamo sapere che a! = B? In particolare quando l'unica caratteristica nota delle due variabili è esattamente la stessa.
LJ2

44

Perché {}[true]non restituisce true, ma undefined, ed undefinedè valutato come false:

http://jsfiddle.net/67GEu/

'use strict';
var b = {}[true];
alert(b); // undefined
b = !{}[true];
alert(b); // true

21
Se si valuta {}[true]in una console, si ottiene [true], poiché {}viene interpretato come un blocco di codice vuoto, non come un oggetto. Riguarda il contesto e l'ambiguità di {}.
IMSoP

1
@IMSoP ma perché {key:"value"}[1,2,3];valuta anche [1,2,3]?
t.niese

3
@ t.niese, perché viene analizzato come un blocco di istruzioni contenente un'etichetta ( key:) e una stringa letterale ( "value"), seguita da una matrice. Il parser non vede ancora un oggetto letterale.
Frédéric Hamidi,

1
@ FrédéricHamidi ah sì, questo è tutto. Ho represso le etichette ^^
t.niese

1
@dooxe Leggi le altre risposte; riguarda tutto il contesto in cui viene interpretato. Se lo avvolgi alert()o console.log()o lo assegni a una variabile, stai modificando il contesto, motivo per cui non si comporta allo stesso modo in cui è stato digitato da solo in una console.
IMSoP

27

Perché

{}[true]

valuta undefinede lo !undefinedè true.

Da @schlingel:

trueè usato come chiave e {}come mappa hash. Non esiste una proprietà con la chiave, truequindi restituisce undefined. Non undefinedè true, come previsto.

Sessione console ( Node.js [0.10.17] ):

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

Tuttavia, in Google Chrome console di :

> !{}[true]
true

Quindi, nessuna incoerenza. Probabilmente stai utilizzando una versione precedente della VM JavaScript. Per coloro che necessitano di ulteriori prove:

Inserisci qui la descrizione dell'immagine

AGGIORNARE

Con Firefox , valuta anche true:

Inserisci qui la descrizione dell'immagine


Non se lo fai eval('{}[true]')o lo digiti per console. Quindi ad es. Als {}"test"è testo addirittura lo {key:"value"}"test"è test.
t.niese,

Interessante, in quale motore js lo testate?
t.niese,

@ t.niese L'ho appena digitato nella mia console del nodo, e questo è quello che ho ottenuto.
Giochi Brainiac,

Solo per curiosità. Fa {}[true];(con la ;) di ritorno [true]per voi, perché qui lo fa?
t.niese,

2
Motivo per i ragazzi downvote? C'è una risposta quasi identica a questa con 8 voti e ottengo il voto negativo? Cos'ho fatto di sbagliato?
Giochi Brainiac,

23

Il motivo della confusione è dovuto a un fraintendimento della tua prima affermazione:

{}[true] è [true]

Quello che vedi quando lo corri è il risultato di un'ambiguità. Javascript ha un insieme definito di regole su come gestire le ambiguità come questa e, in questo caso, suddivide ciò che vedi come un'istruzione signle in due istruzioni separate.

Quindi JavaScript vede il codice sopra come due istruzioni separate: in primo luogo, c'è un {}e poi c'è un completamente separato [true]. La seconda affermazione è ciò che ti sta dando il risultato [true]. La prima affermazione {}viene effettivamente completamente ignorata.

Puoi dimostrarlo provando quanto segue:

({}[true])

cioè racchiudendo il tutto tra parentesi per forzare l'interprete a leggerlo come una singola istruzione.

Ora vedrai che il valore effettivo della tua affermazione è undefined . (questo ci aiuterà anche più tardi a capire la parte successiva)

Ora sappiamo che la parte iniziale della tua domanda è un'aringa rossa, quindi passiamo alla parte finale della domanda:

Quindi, perché! {} [True] viene valutato come vero?

Qui, abbiamo la stessa affermazione, ma con un !allegato in primo piano.

In questo caso, le regole di Javascript indicano che valuta l'intera cosa come una singola istruzione.

Fai riferimento a ciò che è accaduto quando abbiamo racchiuso tra parentesi la precedente dichiarazione; abbiamo ottenuto undefined. Questa volta, stiamo effettivamente facendo la stessa cosa, ma mettendoci !di fronte. Quindi il tuo codice può essere semplificato come !undefined, che è true.

Spero che questo lo spieghi un po '.

È una bestia complessa, ma la lezione da imparare qui è usare parentesi attorno alle tue affermazioni quando le valuti nella console, per evitare risultati spuri come questo.


2
Non credo {}[true]sia esattamente invalido , solo ambiguo . Può essere interpretato come "blocco di codice vuoto seguito da array letterale" o "oggetto letterale senza proprietà, di cui si accede a una proprietà". Non so se il primo sia tecnicamente un caso di ASI (molte lingue non metterebbero comunque un punto e virgola), ma è l'interpretazione sensibile al contesto che è il cuore del problema.
IMSoP

@IMSoP - Avevo già modificato la risposta prima di pubblicare il commento. :)
Spudley,

1
Dice ancora "{} [true] in realtà non è affatto valido" proprio all'inizio della risposta.
IMSoP

Inoltre, OP non ha detto " {}[true]è true" hanno detto " {}[true]è [true]", che è una delle due interpretazioni valide dell'affermazione ambigua.
IMSoP

14

{}[true]lo è undefined. Per trovarlo scrivi questo:

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

o semplicemente:

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

Sappiamo che lo !undefinedè true.


Dalla risposta di @Benjamin Gruenbaum :

Gli strumenti di Chrome Dveloper eseguono le seguenti operazioni :

  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;
  }

Quindi, in sostanza, esegue un calloggetto con l'espressione. L'espressione è:

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

Quindi, come puoi vedere, l'espressione viene valutata direttamente, senza la parentesi avvolgente.

Ulteriori informazioni sono disponibili in questa domanda .


10

Le risposte qui sono buone, ecco una suddivisione in pseudo-codice:

  • {}['whatever'] = blocco vuoto, NewArray ('qualunque') = NewArray ('qualunque' ')
  • {}[true] = blocco vuoto, NewArray (true) = NewArray (true)
  • !{}['whatever'] = LogicalNOT (convertToBool (NewObject.whatever)) = LogicalNOT (convertToBool (undefined)) = LogicalNOT (false) = true
  • ({}['whatever']) = Raggruppamento (NewObject.whatever) = Raggruppamento (non definito) = non definito

8

Ciò accade perché {}nel tuo significato non è una presentazione letterale di Object, ma un ambito vuoto (o un blocco di codice vuoto):

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

Valuta solo il codice all'interno dell'ambito e quindi ti mostra l'array.

E dal tuo

!{}[true]

Converte semplicemente in questo ambito e restituisce lo stesso array vero. Non ci sono bool check in questo codice.

E se proverai a controllare il risultato {}[true], otterrai false:

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

Poiché non esiste più alcun ambito.

Quindi !nella tua domanda fai lo stesso di:

!function() {
   //...
}

Questo è più facile da vedere se lo fai var x = {}; x[true].
Chris Hayes,

1
Non sono sicuro di cosa intendi con "converti in questo ambito"; Credo con il principale !esso viene interpretato come un oggetto vuoto, non portata, e questa è la discrepanza.
IMSoP

6
  • {} è un oggetto senza proprietà.
  • Poiché []segue immediatamente un oggetto, significa "Accedi a una proprietà con questo nome" e non "Crea un array"
  • true è un valore booleano, ma viene utilizzato come nome di proprietà, quindi viene trasmesso a una stringa ("true" )
  • L'oggetto non ha una proprietà chiamata true(poiché non ha proprietà) così {}['true']èundefined
  • !undefinedlancia undefinedun booleano (false )
  • L'operatore non si trasforma falsein true.

2
Nel caso di {}[true](senza altro contesto), non{} è un oggetto senza proprietà, è un blocco di codice vuoto.
IMSoP


4

Giochiamo un po 'di più!

Per prima cosa, divertiamoci un po '!:

//----------#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, proviamo a capire questi comportamenti folli, uno per uno:

1) Qui, {}viene analizzato come un blocco di codice vuoto. Senza assegnazione, negazione, raggruppamento (con parentesi) o qualsiasi sintassi che indica al parser che si {}tratta di un oggetto letterale, il presupposto predefinito è di pensare che sia semplicemente un blocco vuoto inutile.

Questa è una prova di questo comportamento:

{ alert(123) }[true]

Il codice sopra mostrerà l'avviso normalmente e verrà valutato come [true], allo stesso modo{}[true] .

Dichiarazioni di blocco senza punto e virgola

Un'istruzione di tipo blocco non ha bisogno di un punto e virgola dopo di essa.

Per esempio:

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

Vengono visualizzati entrambi gli avvisi.

Quindi, possiamo vedere che un'istruzione di blocco vuota, senza punto e virgola, è valida e semplicemente non fa nulla. In questo modo, quando si accede {}[true]alla Console Strumenti di sviluppo (o Firebug), il valore valutato sarà il valore dell'ultima espressione espressione . In questo caso, l'ultima istruzione espressione è [true].

2) In un contesto di assegnazione, il parser si assicurerà che {}sia un oggetto letterale. Quando si var a = {}[true], si rimuove qualsiasi ambiguità e si annulla il parser che {}non è un'istruzione di blocco.
Quindi, qui, stai cercando di ottenere un valore con una chiave "true"da un oggetto vuoto. Ovviamente, non esiste una coppia chiave-valore con questo nome chiave. In questo modo, la variabile a non è definita.

Parole riservate come chiavi dell'oggetto

ECMAScript 5 consente alle chiavi dell'oggetto di essere parole riservate. Quindi, le seguenti chiavi sono legali:

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

3) La stessa spiegazione dell'esempio 1 . Ma ... Se la { b: 12345 }parte viene trattata come un'istruzione block, qual è il tipo di b: 12345istruzione ??

... (?????)

È una dichiarazione dell'etichetta , l'hai già vista prima ... È usata nei loop e dentro switch. Ecco alcuni link interessanti sulle dichiarazioni delle etichette: 1 , (2) [Il modo migliore per interrompere i loop nidificati in Javascript? , (3) [ Come interrompere i loop nidificati in JavaScript? .

NOTA: prova a valutare questo:

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

Le istruzioni dell'etichetta non possono essere separate dall'operatore virgola , è necessario separarle con un punto e virgola. Quindi questo è valido:{a: 1; b: 2}

4) Vedi le spiegazioni per gli esempi 1 e 3 ...

5) Ancora una volta, ci { b: 12345 }viene trattato come un blocco di codice e si sta tentando di accedere a una proprietà di un blocco di codice utilizzando la notazione punto e, ovviamente, ciò non è consentito e il parser genera "Unexpected token :"un'eccezione.

6) Il codice è quasi identico all'esempio precedente, ma circondando l' { b: 12345 }istruzione con l' operatore di raggruppamento di espressioni , il parser saprà che è un oggetto. In questo modo, sarai in grado di accedere "b"normalmente alla proprietà.

7) Ricorda l'esempio 2 , qui abbiamo un compito, il parser sa che { b: 12345 }è un oggetto.

8) Identico all'esempio sopra, ma invece della notazione punto, qui stiamo usando la notazione parentesi .

9) Ho già detto che questa "identifier: value"sintassi all'interno di un'istruzione block è un'etichetta. Ma devi anche sapere che il nome di un'etichetta non può essere una parola chiave riservata (l'opposto dei nomi delle proprietà degli oggetti). Quando abbiamo provato a definire un'etichetta chiamata "true", abbiamo ottenuto un SyntaxError.

10) Ancora una volta, abbiamo a che fare con un oggetto. Nessun problema con le parole riservate qui. =)

11) Infine, abbiamo questo:!{}[true]

Separiamo le cose qui:

a) Facendo una negazione, stiamo informando il parser che {}è un oggetto .

b) Come mostrato nell'esempio 2 , un {}oggetto non ha una proprietà chiamata true, quindi questa espressione verrà valutata undefined.

c) Il risultato finale è la negazione del undefinedvalore. Javascript esegue la conversione del tipo di implicità e il undefinedvalore è errato .

d) Quindi, la negazione di falseè ... true!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.