¿Por qué! {} [True] se evalúa como verdadero en JavaScript?

131

{}[true]es [true]y ![true]debe ser false.

Entonces, ¿por qué !{}[true]evaluar a true?

usuario2430508
fuente
30
var o = {}; o[true] === undefined.
azz
2
La explicación aquí probablemente será muy similar a las rarezas discutidas en esta pregunta anterior
IMSoP
45
"Porque Javascript es tonto" probablemente no sea la respuesta que estás buscando.
georg
2
Como se mencionó, si obtiene {}[true] === [true]de una consola, es porque se trata {}como un bloque de código vacío, no como un objeto.
azz
3
si puede ayudar, intente comparar {}y ({})en su consola (o {}[true]y ({})[true]). Además, como nadie lo mencionó, el objeto [verdadero] se evalúa como objeto ["verdadero"].
BiAiB

Respuestas:

172

Creo que eso se debe a que plain {}[true]se analiza como un bloque de instrucciones vacío (no un literal de objeto) seguido de una matriz que contiene true, que es true.

Por otro lado, la aplicación del !operador hace que el analizador interprete {}como un objeto literal, por lo que lo siguiente se {}[true]convierte en un acceso de miembro que devuelve undefined, y de !{}[true]hecho true(como !undefinedestrue ).

Frédéric Hamidi
fuente
25
El hecho de que! Undefined es cierto, por otro lado, sigue siendo inexcusable.
evilcandybag
87
@evilcandybag: Absolutamente no. undefinedes falso (algo en lo que confiamos a menudo if (obj.maybeExists) ...), por lo que tiene mucho sentido lógico que !undefinedsea ​​cierto.
josh3736
8
@ Josh, creo que evilcandybag preferiría un comportamiento similar nullen algunos idiomas, con !undefinedser igual a undefined. Sin embargo, ese no es el caso en Javascript.
Frédéric Hamidi
66
@evilcandybag: solo tiene sentido lógico decir que algo que es not undefined( !undefined), por lo tanto, debe definirse. Si algo se define, generalmente se interpreta como true.
OozeMeister
77
@Cruncher Si a no está definido yb no está definido, ¿cómo podemos saber que a! = B? Particularmente cuando la única característica conocida de las dos variables es exactamente la misma.
LJ2
44

Porque {}[true]no regresa true, pero undefined, y undefinedse evalúa como false:

http://jsfiddle.net/67GEu/

'use strict';
var b = {}[true];
alert(b); // undefined
b = !{}[true];
alert(b); // true
dooxe
fuente
21
Si evalúa {}[true]en una consola, obtiene [true], porque {}se interpreta como un bloque de código vacío, no como un objeto. Se trata del contexto y la ambigüedad de {}.
IMSoP
1
@IMSoP pero ¿por qué {key:"value"}[1,2,3];también evalúa [1,2,3]?
t.niese
3
@ t.niese, porque se analiza como un bloque de instrucciones que contiene una etiqueta ( key:) y un literal de cadena ( "value"), seguido de una matriz. El analizador aún no ve un objeto literal.
Frédéric Hamidi
1
@ FrédéricHamidi ah sí, eso es todo. Reprimí etiquetas ^^
t.niese
1
@dooxe Lee las otras respuestas; Se trata del contexto en el que se interpreta. Si lo envuelve en alert()o console.log(), o lo asigna a una variable, está cambiando el contexto, por lo que no se comporta de la misma manera que se escribe solo en una consola.
IMSoP
27

Porque

{}[true]

evalúa a undefined, y !undefinedes true.

De @schlingel:

truese usa como clave y {}como mapa hash. No existe una propiedad con la clave, truepor lo que se devuelve undefined. No undefinedes true, como se esperaba.

Sesión de consola ( Node.js [0.10.17] ):

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

Sin embargo, en la consola Google Chrome :

> !{}[true]
true

Entonces, no hay inconsistencias. Probablemente esté utilizando una versión anterior de la máquina virtual de JavaScript. Para aquellos que necesitan más evidencia:

Ingrese la descripción de la imagen aquí

ACTUALIZAR

Con Firefox , también se evalúa para true:

Ingrese la descripción de la imagen aquí

Juegos Brainiac
fuente
No si lo haces eval('{}[true]')o lo escribes en la consola. Entonces, por ejemplo, als {}"test"es testo incluso {key:"value"}"test"es test.
t.niese
Interesante, ¿en qué motor js pruebas eso?
t.niese
@ t.niese Lo acabo de escribir en la consola de mi nodo, y esto es lo que obtuve.
Juegos Brainiac
Solamente por curiosidad. ¿ {}[true];(Con el ;) vuelve [true]por ti, porque aquí lo hace?
t.niese
2
¿Motivo para los chicos con voto negativo? Hay una respuesta casi idéntica a esta con 8 votos, y obtengo el voto negativo. ¿Qué hice mal?
Juegos Brainiac
23

La razón de la confusión se debe a un malentendido de su primera afirmación:

{}[true] es [true]

Lo que ves cuando lo ejecutas es el resultado de una ambigüedad. Javascript tiene un conjunto definido de reglas sobre cómo manejar ambigüedades como esta, y en este caso, divide lo que ve como una declaración de señalización en dos declaraciones separadas.

Entonces, Javascript ve el código anterior como dos declaraciones separadas: en primer lugar, hay un {}, y luego hay un completamente separado [true]. La segunda afirmación es lo que te está dando el resultado [true]. La primera declaración {}es efectivamente ignorada por completo.

Puede probar esto intentando lo siguiente:

({}[true])

es decir, envolver todo entre paréntesis para obligar al intérprete a leerlo como una sola declaración.

Ahora verá que el valor real de su estado de cuenta es undefined . (Esto también nos ayudará más tarde a comprender la siguiente parte)

Ahora sabemos que la parte inicial de su pregunta es una pista falsa, así que pasemos a la parte final de la pregunta:

Entonces, ¿por qué! {} [True] se evalúa como verdadero?

Aquí, tenemos la misma declaración, pero con un ! anexo al frente.

En este caso, las reglas de Javascript le dicen que evalúe todo como una sola declaración.

Refiérase a lo que sucedió cuando envolvimos la declaración anterior entre paréntesis; llegamos undefined. Esta vez, efectivamente estamos haciendo lo mismo, pero poniendo un !frente a eso. Entonces su código puede simplificarse como !undefined, que estrue .

Esperemos que eso lo explique un poco.

Es una bestia compleja, pero la lección que debe aprender aquí es usar corchetes alrededor de sus declaraciones al evaluarlas en la consola, para evitar resultados espurios como este.

Spudley
fuente
2
No creo que {}[true]sea inválido exactamente, solo ambiguo . Puede interpretarse como "bloque de código vacío seguido de literal de matriz" o "literal de objeto sin propiedades, al que se accede a una propiedad". No sé si el primero es técnicamente un caso de ASI (de todos modos, muchos idiomas no pondrían un punto y coma), pero es la interpretación sensible al contexto el núcleo del problema.
IMSoP
@IMSoP: ya había editado la respuesta antes de que publicaras el comentario. :)
Spudley
1
Todavía dice "{} [verdadero] no es realmente válido en absoluto" justo al comienzo de la respuesta.
IMSoP
Además, OP no dijo " {}[true]es true", dijeron " {}[true]es [true]", que es una de las dos interpretaciones válidas de la declaración ambigua.
IMSoP
14

{}[true]es undefined. Para encontrar eso escribe esto:

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

o simplemente:

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

Sabemos que !undefinedes true.


De la respuesta de @Benjamin Gruenbaum :

Las herramientas de Chrome Dveloper hacen lo siguiente :

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

Entonces, básicamente, realiza un callen el objeto con la expresión. La expresión es:

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

Entonces, como puede ver, la expresión se está evaluando directamente, sin el paréntesis de ajuste.

Se puede encontrar más información en esta pregunta .

Ionică Bizău
fuente
10

Las respuestas aquí son buenas, aquí hay un desglose en pseudocódigo:

  • {}['whatever'] = bloque vacío, NewArray ('lo que sea') = NewArray ('lo que sea')
  • {}[true] = bloque vacío, NewArray (true) = NewArray (true)
  • !{}['whatever'] = LogicalNOT (convertToBool (NewObject.whatever)) = LogicalNOT (convertToBool (undefined)) = LogicalNOT (false) = verdadero
  • ({}['whatever']) = Agrupación (NewObject.whatever) = Agrupación (indefinido) = indefinido
Ben Lesh
fuente
8

Esto sucede porque, {}en su significado, no es una presentación literal de Object, sino un alcance vacío (o un bloque de código vacío):

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

Simplemente evalúa el código dentro del alcance y luego le muestra su matriz.

Y de tu

!{}[true]

Simplemente convierte a int este alcance y devuelve la misma matriz verdadera. No hay cheques de bool en este código.

Y si intenta comprobar el resultado {}[true], obtendrá su false:

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

Como ya no hay más alcance.

Entonces, !en su pregunta, haga lo mismo que:

!function() {
   //...
}
antyrat
fuente
Esto se ve más fácilmente si lo haces var x = {}; x[true].
Chris Hayes
1
No estoy seguro de lo que quieres decir con "se convierte a int en este ámbito"; Creo que con el principal !que se interpreta como un objeto vacío, no alcance, y esta es la discrepancia.
IMSoP
6
  • {} es un objeto sin propiedades.
  • Como []sigue inmediatamente a un objeto, significa "Acceder a una propiedad de este nombre" y no "Crear una matriz"
  • truees un booleano, pero se está utilizando como un nombre de propiedad, por lo que se convierte en una cadena ( "true")
  • El objeto no tiene una propiedad llamada true(ya que no tiene propiedades) por lo que {}['true']esundefined
  • !undefinedse convierte undefineden un booleano ( false)
  • El operador no se convierte falseen true.
Quentin
fuente
2
En el caso de {}[true](sin otro contexto), no{} es un objeto sin propiedades, es un bloque de código vacío.
IMSoP
4

¡Juguemos un poco más!

Primero, ¡divirtámonos !:

//----------#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, tratemos de entender estos comportamientos locos, uno por uno:

1) Aquí, el {}se analiza como un bloque de código vacío. Sin una asignación, negación, agrupación (con paréntesis) o cualquier sintaxis que indique al analizador que este {}es un objeto literal, la suposición predeterminada es pensar que es simplemente un bloque vacío inútil.

Esta es una prueba de este comportamiento:

{ alert(123) }[true]

El código anterior mostrará la alerta normalmente y se evaluará como [true], de la misma manera {}[true].

Declaraciones de bloque sin punto y coma

Una declaración de tipo bloque no necesita un punto y coma después de ella.

Por ejemplo:

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

Se muestran ambas alertas.

Entonces, podemos ver que una declaración de bloque vacía, sin punto y coma, es válida y simplemente no hace nada. De esta manera, cuando ingrese {}[true]en la consola de herramientas de desarrollador (o Firebug), el valor evaluado será el valor de la última declaración de expresión . En este caso, la última declaración de expresión es [true].

2) En un contexto de asignación, el analizador se asegurará de que {}sea ​​un objeto literal. Cuando haces var a = {}[true], eliminas cualquier ambigüedad y avisas al analizador que {}no es una declaración de bloque.
Entonces, aquí, estás tratando de obtener un valor con una clave "true"de un objeto vacío. Obviamente, no hay un par clave-valor con este nombre clave. De esta manera, la variable a no está definida.

Palabras reservadas como claves de objeto

ECMAScript 5 permite que las claves de objeto sean palabras reservadas. Entonces, las siguientes claves son legales:

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

3) La misma explicación del ejemplo 1 . Pero ... Si la { b: 12345 }parte se trata como una declaración de bloque, ¿cuál es el tipo de b: 12345declaración?

... (?????)

Es una declaración de etiqueta , ya la viste antes ... Se usa en bucles y adentro switch. Aquí hay algunos enlaces interesantes sobre declaraciones de etiquetas: 1 , (2) [¿La mejor manera de romper los bucles anidados en Javascript? , (3) [ ¿Cómo romper bucles anidados en javascript? .

NOTA: solo trate de evaluar esto:

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

Las declaraciones de etiquetas no pueden ser separadas por el operador de coma , necesitaría separarlas con un punto y coma. Entonces esto es válido:{a: 1; b: 2}

4) Véanse las explicaciones de los ejemplos 1 y 3 ...

5) Una vez más, se nos { b: 12345 }trata como un bloque de código, y está intentando acceder a una propiedad de un bloque de código utilizando la notación de puntos , y obviamente, esto no está permitido, y el analizador arroja una "Unexpected token :"excepción.

6) El código es casi idéntico al ejemplo anterior, pero al rodear la { b: 12345 }declaración con el operador de agrupación de expresiones , el analizador sabrá que es un objeto. De esta manera, podrá acceder a la "b"propiedad normalmente.

7) Recuerde el ejemplo 2 , tenemos una asignación aquí, el analizador sabe que { b: 12345 }es un objeto.

8) Idéntico al ejemplo anterior, pero en lugar de la notación de puntos, aquí estamos usando la notación de corchetes .

9) Ya dije que esta "identifier: value"sintaxis dentro de una declaración de bloque es una etiqueta. Pero también debe saber que un nombre de etiqueta no puede ser una palabra clave reservada (lo opuesto a los nombres de propiedad de objeto). Cuando intentamos definir una etiqueta llamada "true", obtuvimos un SyntaxError.

10) Nuevamente, estamos tratando con un objeto. No hay problemas al usar palabras reservadas aquí. =)

11) Finalmente, tenemos esto:!{}[true]

Separemos las cosas aquí:

a) Al hacer una negación, estamos informando al analizador que {}es un objeto .

b) Como se muestra en el ejemplo 2 , un {}objeto no tiene una propiedad llamada true, por lo que esta expresión se evaluará como undefined.

c) El resultado final es la negación del undefinedvalor. Javascript realiza una conversión de tipo de implicidad , y el undefinedvalor es falso .

d) Entonces, la negación de falsees ... true!

Alcides Queiroz Aguiar
fuente