Por que! {} [True] é avaliado como verdadeiro em JavaScript?

131

{}[true]é [true]e ![true]deve ser false.

Então, por que !{}[true]avaliar para true?

user2430508
fonte
30
var o = {}; o[true] === undefined.
azz
2
A explicação aqui provavelmente será muito semelhante às esquisitices discutidas nesta pergunta anterior
IMSoP
45
"Como o Javascript é bobo" provavelmente não é a resposta que você está procurando.
quer
2
Como mencionado, se você estiver obtendo {}[true] === [true]de um console, é porque está tratando {}como um bloco de código vazio, não como um objeto.
azz
3
se puder ajudar, tente comparar {}e ({})no seu console (ou {}[true]e ({})[true]). Além disso, como ninguém mencionou, o objeto [true] é avaliado como objeto ["true"].
BiAiB 31/10

Respostas:

172

Eu acredito que é porque plain {}[true]é analisado como um bloco de instrução vazio (não um objeto literal) seguido por uma matriz que contém true, o que é true.

Por outro lado, a aplicação do !operador faz com que o analisador interprete {}como um objeto literal, para que o seguinte {}[true]se torne um acesso de membro que retorne undefinede !{}[true]seja true(como !undefinedé true).

Frédéric Hamidi
fonte
25
O fato de que undefined é verdade, por outro lado, ainda é indesculpável.
evilcandybag
87
@evilcandybag: absolutamente não. undefinedé falso (algo em que confiamos frequentemente - if (obj.maybeExists) ...), por isso faz todo o sentido lógico que !undefinedé verdadeiro.
josh3736
8
@ Josh, acho que evilcandybag preferiria um comportamento semelhante ao nullde algumas línguas, !undefinedsendo igual a undefined. Esse não é o caso do Javascript.
Frédéric Hamidi 31/10
6
@evilcandybag: faz sentido lógico dizer que algo que é not undefined( !undefined) deve, portanto, ser definido. Se algo é definido, geralmente é interpretado como true.
OozeMeister
7
@ Cruncher Se a é indefinido eb é indefinido, como podemos saber que a! = B? Especialmente quando a única característica conhecida das duas variáveis ​​é exatamente a mesma.
LJ2
44

Porque {}[true]não retorna true, mas undefined, e undefinedé avaliado como false:

http://jsfiddle.net/67GEu/

'use strict';
var b = {}[true];
alert(b); // undefined
b = !{}[true];
alert(b); // true
dooxe
fonte
21
Se você avaliar {}[true]em um console, obtém [true], porque o {}é interpretado como um bloco de código vazio, não como um objeto. É tudo sobre o contexto e a ambiguidade de {}.
IMSoP
1
@IMSoP, mas por que {key:"value"}[1,2,3];também avalia [1,2,3]?
t.niese
3
@ t.niese, porque é analisado como um bloco de instruções contendo um rótulo ( key:) e uma string literal ( "value"), seguida por uma matriz. O analisador ainda não vê um objeto literal.
Frédéric Hamidi 31/10
1
@ FrédéricHamidi ah sim, é isso. Eu reprimi os rótulos ^^
t.niese 31/10
1
@dooxe Leia as outras respostas; é tudo sobre o contexto em que é interpretado. Se você envolvê-lo alert()ou console.log(), ou atribuí-lo a uma variável, você está alterando o contexto, e é por isso que ele não se comporta da mesma maneira que digitado sozinho em um console.
IMSOP
27

Porque

{}[true]

avalia undefinede !undefinedé true.

From @schlingel:

trueé usado como chave e {}como mapa de hash. Não existe uma propriedade com a chave, trueportanto ela retorna undefined. Não undefinedé true, como esperado.

Sessão do console ( Node.js [0.10.17] ):

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

No entanto, no console do Google Chrome :

> !{}[true]
true

Portanto, sem inconsistências. Você provavelmente está usando uma versão antiga da VM JavaScript. Para aqueles que precisam de mais evidências:

Digite a descrição da imagem aqui

ATUALIZAR

Com o Firefox , ele também avalia true:

Digite a descrição da imagem aqui

Jogos Brainiac
fonte
Não, se você eval('{}[true]')digitar ou digitar no console. Então, por exemplo, als {}"test"é testou mesmo {key:"value"}"test"é test.
t.niese
Interessante, em qual mecanismo js você testa isso?
t.niese
@ t.niese Acabei de digitar no console do nó e foi isso que obtive.
Games Brainiac
Só por curiosidade. Será que {}[true];(com a ;) de retorno [true]para você, porque aqui ele faz?
t.niese
2
Motivo para pessoas com votos negativos? Existe uma resposta quase idêntica a esta com 8 votos, e eu recebo o voto negativo? O que eu fiz errado?
Games Brainiac
23

O motivo da confusão se deve a um mal-entendido de sua primeira afirmação:

{}[true] é [true]

O que você vê quando executa é o resultado de uma ambiguidade. O Javascript possui um conjunto definido de regras sobre como lidar com ambiguidades como essa e, nesse caso, divide o que você vê como uma declaração de sinalização em duas declarações separadas.

Portanto, o Javascript vê o código acima como duas instruções separadas: Primeiro, existe um {}e, depois, um totalmente separado [true]. A segunda afirmação é o que está lhe dando o resultado [true]. A primeira afirmação{} é efetivamente totalmente ignorada.

Você pode provar isso tentando o seguinte:

({}[true])

ou seja, colocar tudo entre colchetes para forçar o intérprete a lê-lo como uma única declaração.

Agora você verá que o valor real de sua declaração é undefined. (isso também nos ajudará mais tarde a entender a próxima parte)

Agora sabemos que a parte inicial da sua pergunta é um arenque vermelho, então vamos para a parte final da pergunta:

Então, por que! {} [True] avalia como true?

Aqui, temos a mesma declaração, mas com um !anexo à frente.

Nesse caso, as regras do Javascript dizem para avaliar a coisa toda como uma única instrução.

Consulte o que aconteceu quando colocamos a declaração anterior entre colchetes; nós temos undefined. Desta vez, estamos efetivamente fazendo a mesma coisa, mas colocando a !frente dela. Portanto, seu código pode ser simplificado como !undefined, o que é true.

Espero que isso explique um pouco.

É um animal complexo, mas a lição a aprender aqui é usar colchetes em torno de suas declarações ao avaliá-las no console, para evitar resultados espúrios como este.

Spudley
fonte
2
Não acho que {}[true]seja inválido exatamente, apenas ambíguo . Pode ser interpretado como "bloco de código vazio seguido por matriz literal" ou "literal de objeto sem propriedades, das quais uma propriedade está sendo acessada". Não sei se o primeiro é tecnicamente um caso de ASI (muitas línguas não colocariam ponto-e-vírgula lá de qualquer maneira), mas é a interpretação sensível ao contexto que é o cerne da questão.
IMSoP
@IMSoP - Eu já tinha editado a resposta antes de você postar o comentário. :)
Spudley
1
Ele ainda diz que "{} [true] não é realmente válido" logo no início da resposta.
IMSoP
Além disso, o OP não disse " {}[true]é true" eles disseram " {}[true]é [true]", que é uma das duas interpretações válidas da declaração ambígua.
IMSoP
14

{}[true]é undefined. Para descobrir que escreva isso:

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

ou simplesmente:

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

Nós sabemos que !undefinedé isso true.


Da resposta de @Benjamin Gruenbaum :

As ferramentas de envelope do Chrome fazem o seguinte :

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

Então, basicamente, ele executa um callno objeto com a expressão A expressão sendo:

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

Portanto, como você pode ver, a expressão está sendo avaliada diretamente, sem o parêntese de quebra automática.

Mais informações podem ser encontradas nesta pergunta .

Ionică Bizău
fonte
10

As respostas aqui são boas, aqui está um detalhamento do pseudo-código:

  • {}['whatever'] = bloco vazio, NewArray ('qualquer coisa') = NewArray ('qualquer coisa')
  • {}[true] = bloco vazio, NewArray (true) = NewArray (true)
  • !{}['whatever'] = LogicalNOT (convertToBool (NewObject.whatever)) = LogicalNOT (convertToBool (indefinido)) = LogicalNOT (false) = true
  • ({}['whatever']) = Agrupamento (NewObject.whatever) = Agrupamento (indefinido) = indefinido
Ben Lesh
fonte
8

Isso acontece porque, {}no seu significado, não é uma apresentação literal de Object, mas um escopo vazio (ou bloco de código vazio):

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

Ele apenas avalia o código dentro do escopo e mostra sua matriz.

E do seu

!{}[true]

Apenas converte para int esse escopo e retorna a mesma matriz true. Não há verificações de booleano neste código.

E se você tentar verificar o resultado {}[true], obterá o seu false:

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

Como não há mais escopo.

Portanto, !na sua pergunta, faça o mesmo:

!function() {
   //...
}
antyrat
fonte
Isso é mais facilmente visto se você o fizer var x = {}; x[true].
Chris Hayes
1
Não sei ao certo o que você quer dizer com "converte para int este escopo"; Eu acho que, com o líder, !ele é interpretado como um objeto vazio, não como escopo, e essa é a discrepância.
IMSoP
6
  • {} é um objeto sem propriedades.
  • Como []segue imediatamente um objeto, significa "Acessar uma propriedade com esse nome" e não "Criar uma matriz"
  • trueé um booleano, mas está sendo usado como um nome de propriedade para que seja convertido em uma string ( "true")
  • O objeto não possui uma propriedade chamada true(uma vez que não possui propriedades), portanto, {}['true']éundefined
  • !undefinedlança undefinedpara um booleano ( false)
  • O operador not se falsetransforma true.
Quentin
fonte
2
No caso de {}[true](sem outro contexto), não{} é um objeto sem propriedades, é um bloco de código vazio.
IMSoP
4

Vamos jogar um pouco mais!

Primeiro, vamos nos divertir!

//----------#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, vamos tentar entender esses comportamentos malucos, um por um:

1) Aqui, o {}é analisado como um bloco de código vazio. Sem uma atribuição, negação, agrupamento (entre parênteses) ou qualquer sintaxe que indique ao analisador que este {}é um objeto literal, a suposição padrão é pensar que é simplesmente um bloco vazio inútil.

Esta é uma prova desse comportamento:

{ alert(123) }[true]

O código acima mostrará o alerta normalmente e será avaliado como [true], da mesma maneira que {}[true]é.

Declarações de bloco sem ponto e vírgula

Uma instrução do tipo bloco não precisa de um ponto e vírgula após ela.

Por exemplo:

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

Os dois alertas são mostrados.

Portanto, podemos ver que uma declaração de bloco vazio, sem ponto e vírgula, é válida e simplesmente não faz nada. Dessa forma, quando você entra {}[true]no console das Ferramentas do desenvolvedor (ou Firebug), o valor avaliado será o valor da última declaração de expressão . Neste caso, a última declaração expressão é [true].

2) Em um contexto de atribuição, o analisador garantirá que {}seja um objeto literal. Ao fazer var a = {}[true], você remove qualquer ambiguidade e informa o analisador que {}não é uma instrução de bloco.
Então, aqui, você está tentando obter um valor com uma chave "true"de um objeto vazio. Obviamente, não há par de valores-chave com esse nome de chave. Dessa forma, a variável a é indefinida.

Palavras reservadas como chaves de objeto

O ECMAScript 5 permite que as chaves do objeto sejam palavras reservadas. Portanto, as seguintes chaves são legais:

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

3) A mesma explicação do exemplo 1 . Mas ... Se a { b: 12345 }peça é tratada como uma declaração de bloco, qual é o tipo da b: 12345declaração?

... (?????)

É uma declaração de etiqueta , você já a viu antes ... É usada em loops e in switch. Aqui estão alguns links interessantes sobre instruções de rótulo: 1 , (2) [ Melhor maneira de interromper loops aninhados em Javascript? , (3) [ Como quebrar loops aninhados em javascript? .

NOTA: Apenas tente avaliar isso:

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

As instruções do rótulo não podem ser separadas pelo operador de vírgula ; você precisará separá-las com um ponto e vírgula. Portanto, isso é válido:{a: 1; b: 2}

4) Veja as explicações para os exemplos 1 e 3 ...

5) Mais uma vez, estamos { b: 12345 }sendo tratados como um bloco de código e você está tentando acessar uma propriedade de um bloco de código usando a notação de ponto e, obviamente, isso não é permitido, e o analisador lança um"Unexpected token :" exceção.

6) O código é quase idêntico ao exemplo acima, mas cercando a { b: 12345 }instrução com o operador de agrupamento de expressões , o analisador saberá que é um objeto. Dessa forma, você poderá acessar o"b" propriedade normalmente.

7) Lembre-se do exemplo 2 , temos uma atribuição aqui, o analisador sabe que{ b: 12345 } é um objeto.

8) Idêntico ao exemplo acima, mas em vez da notação de ponto, aqui estamos usando a notação de colchete .

9) Eu já disse que essa "identifier: value"sintaxe dentro de uma instrução de bloco é um rótulo. Mas você também deve saber que um nome de rótulo não pode ser uma palavra-chave reservada (o oposto dos nomes de propriedades do objeto). Quando tentamos definir um rótulo chamado "true", obtivemos a SyntaxError.

10) Mais uma vez, estamos lidando com um objeto. Não há problemas ao usar palavras reservadas aqui. =)

11) Finalmente, temos o seguinte:!{}[true]

Vamos separar as coisas aqui:

a) Ao fazer uma negação, informamos ao analisador que {}é um objeto .

b) Como mostrado no exemplo 2 , um {}objeto não possui uma propriedade chamada true, portanto, essa expressão será avaliada comoundefined .

c) O resultado final é a negação de undefinedvalor. Javascript realiza conversão de tipo implícito , e o undefinedvalor é falso .

d) Então, a negação de falseé ... true!

Alcides Queiroz Aguiar
fonte