Pourquoi! {} [True] est-il évalué à true en JavaScript?


131

{}[true]est [true]et ![true]devrait être false.

Alors pourquoi !{}[true]évalue- truet-il?


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

2
L'explication ici sera probablement très similaire aux bizarreries discutées sur cette question précédente
IMSoP

45
"Parce que Javascript est idiot" n'est probablement pas la réponse que vous recherchez.
georg

2
Comme mentionné, si vous obtenez à {}[true] === [true]partir d'une console, c'est parce qu'elle est traitée {}comme un bloc de code vide, pas comme un objet.
azz

3
si cela peut vous aider, essayez de comparer {}et ({})dans votre console (ou {}[true]et ({})[true]). De plus, comme personne ne l'a mentionné, object [true] est évalué à object ["true"].
BiAiB

Réponses:


172

Je pense que c'est parce que plain {}[true]est analysé comme un bloc d'instructions vide (pas un littéral d'objet) suivi d'un tableau contenanttrue , qui est true.

D'autre part, l'application de l' !opérateur fait que l'analyseur est interprété {}comme un objet littéral, de sorte que ce qui suit {}[true]devient un accès membre qui retourne undefined, et !{}[true]est en effet true(tel !undefinedquel true).


25
Le fait que! Indéfini soit vrai, en revanche, est toujours inexcusable.
evilcandybag

87
@evilcandybag: Ce n'est absolument pas le cas. undefinedest faux (quelque chose sur lequel nous nous appuyons souvent - if (obj.maybeExists) ...), donc cela a un sens logique parfait qui !undefinedest vrai.
josh3736

8
@Josh, je pense que evilcandybag préférerait un comportement semblable à celui de nullcertaines langues, avec !undefinedégalité à undefined. Ce n'est cependant pas le cas en Javascript.
Frédéric Hamidi

6
@evilcandybag: il est logique de dire que quelque chose qui est not undefined( !undefined) doit donc être défini. Si quelque chose est défini, il est généralement interprété comme true.
OozeMeister

7
@Cruncher Si a n'est pas défini et que b n'est pas défini, comment pouvons-nous savoir que a! = B? Surtout lorsque la seule caractéristique connue des deux variables est exactement la même.
LJ2

44

Parce {}[true]que ne retourne pas true, mais undefinedet undefinedest évalué comme false:

http://jsfiddle.net/67GEu/

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

21
Si vous évaluez {}[true]dans une console, vous obtenez [true], car le {}est interprété comme un bloc de code vide et non comme un objet. Tout dépend du contexte et de l'ambiguïté de {}.
IMSoP

1
@IMSoP mais pourquoi {key:"value"}[1,2,3];évalue- t-il aussi [1,2,3]?
t.niese

3
@ t.niese, car il est analysé comme un bloc d'instructions contenant une étiquette ( key:) et une chaîne littérale ( "value"), suivies d'un tableau. L'analyseur ne voit toujours pas d'objet littéral.
Frédéric Hamidi

1
@ FrédéricHamidi ah oui, c'est tout. J'ai réprimé les étiquettes ^^
t.niese

1
@dooxe Lisez les autres réponses; tout dépend du contexte dans lequel il est interprété. Si vous l'enveloppez dans alert()ou console.log(), ou l'assignez à une variable, vous modifiez le contexte, c'est pourquoi il ne se comporte pas de la même manière que celui tapé seul dans une console.
IMSoP

27

Car

{}[true]

évalue undefinedet!undefined est true.

De @schlingel:

trueest utilisé comme clé et {}comme carte de hachage. Il n'existe pas de propriété avec la clé truedonc elle retourne undefined. ne pasundefined est true, comme prévu.

Session de console ( Node.js [0.10.17] ):

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

Cependant, dans la console Google Chrome :

> !{}[true]
true

Donc, pas d'incohérences. Vous utilisez probablement une ancienne version de la machine virtuelle JavaScript. Pour ceux qui ont besoin de preuves supplémentaires:

Entrez la description de l'image ici

METTRE À JOUR

Avec Firefox , il évalue également true:

Entrez la description de l'image ici


Pas si vous le faites eval('{}[true]')ou le tapez dans la console. Alors, par exemple, als {}"test"est testou même {key:"value"}"test"est test.
t.niese

Intéressant, dans quel moteur js testez-vous cela?
t.niese

@ t.niese Je viens de le saisir dans ma console de nœud, et c'est ce que j'ai obtenu.
Jeux Brainiac

Juste par curiosité. Est-ce que {}[true];(avec le ;) revient [true]pour vous, parce que c'est le cas ici?
t.niese

2
Raison de voter contre les gars? Il y a une réponse presque identique à celle-ci avec 8 voix, et j'obtiens le vote négatif? Qu'ai-je fait de mal?
Jeux Brainiac du

23

La raison de la confusion est due à une mauvaise compréhension de votre première affirmation:

{}[true] est [true]

Ce que vous voyez lorsque vous l'exécutez est le résultat d'une ambiguïté. Javascript a un ensemble défini de règles sur la façon de gérer des ambiguïtés comme celle-ci, et dans ce cas, il divise ce que vous voyez comme une instruction signle en deux instructions distinctes.

Donc Javascript voit le code ci-dessus comme deux déclarations distinctes: Premièrement, il y a un {}, puis il y a un tout à fait séparé [true]. La deuxième déclaration est ce qui vous donne le résultat [true]. La première déclaration {}est en effet entièrement ignorée.

Vous pouvez le prouver en essayant ce qui suit:

({}[true])

c'est-à-dire mettre le tout entre crochets pour forcer l'interpréteur à le lire comme une seule instruction.

Vous verrez maintenant que la valeur réelle de votre relevé est undefined . (cela nous aidera également plus tard à comprendre la partie suivante)

Nous savons maintenant que la partie initiale de votre question est un hareng rouge, alors passons à la dernière partie de la question:

Alors pourquoi! {} [True] est-il évalué à vrai?

Ici, nous avons la même déclaration, mais avec un !appendice à l'avant de celle-ci.

Dans ce cas, les règles de Javascript lui disent d'évaluer le tout comme une seule instruction.

Reportez-vous à ce qui s'est passé lorsque nous avons mis la déclaration précédente entre crochets; nous avons undefined. Cette fois, nous faisons effectivement la même chose, mais en mettant un !devant. Ainsi, votre code peut être simplifié comme !undefined, ce qui esttrue .

J'espère que cela l'explique un peu.

C'est une bête complexe, mais la leçon à tirer ici est d'utiliser des crochets autour de vos déclarations lors de leur évaluation dans la console, pour éviter de faux résultats comme celui-ci.


2
Je ne pense pas que ce {}[true]soit exactement invalide , juste ambigu . Il peut être interprété comme "bloc de code vide suivi d'un littéral de tableau" ou "littéral d'objet sans propriétés, dont une propriété est en cours d'accès". Je ne sais pas si le premier est techniquement un cas d'ASI (de nombreuses langues ne mettraient pas de point-virgule là-bas de toute façon) mais c'est l'interprétation contextuelle qui est au cœur du problème.
IMSoP

@IMSoP - J'avais déjà modifié la réponse avant de publier le commentaire. :)
Spudley

1
Il dit toujours "{} [vrai] n'est pas réellement valide du tout" dès le début de la réponse.
IMSoP

De plus, OP n'a pas dit « {}[true]est true», ils ont dit « {}[true]est [true]», ce qui est l'une des deux interprétations valides de la déclaration ambiguë.
IMSoP

14

{}[true]est undefined. Pour trouver cela, écrivez ceci:

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

ou simplement:

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

Nous savons que !undefinedc'est true.


D' après la réponse de @Benjamin Gruenbaum :

Les outils de développement Chrome effectuent les opérations suivantes :

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

Donc, fondamentalement, il effectue un callsur l'objet avec l'expression. L'expression étant:

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

Ainsi, comme vous pouvez le voir, l'expression est évaluée directement, sans la parenthèse d'habillage.

Plus d'informations peuvent être trouvées dans cette question .


10

Les réponses ici sont bonnes, voici une ventilation en pseudo-code:

  • {}['whatever'] = bloc vide, NewArray ('peu importe') = NewArray ('peu importe')
  • {}[true] = bloc vide, NewArray (true) = NewArray (true)
  • !{}['whatever'] = LogicalNOT (convertToBool (NewObject.whatever)) = LogicalNOT (convertToBool (undefined)) = LogicalNOT (false) = true
  • ({}['whatever']) = Grouping (NewObject.whatever) = Grouping (undefined) = non défini

8

Cela se produit parce que {}dans votre sens, ce n'est pas une présentation littérale de Object, mais une portée vide (ou un bloc de code vide):

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

Il évalue simplement le code à l'intérieur de la portée, puis vous montre votre tableau.

Et de votre

!{}[true]

Convertit simplement en int cette portée et renvoie le même tableau true. Il n'y a pas de contrôle booléen dans ce code.

Et si vous essayez de vérifier le résultat, {}[true]vous obtiendrez votre false:

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

Comme il n'y a plus de portée.

Donc, !dans votre question, faites la même chose que:

!function() {
   //...
}

Cela se voit plus facilement si vous le faites var x = {}; x[true].
Chris Hayes

1
Je ne suis pas sûr de ce que vous entendez par "convertit en int cette portée"; Je pense qu'avec le début, !il est interprété comme un objet vide, pas comme une portée, et c'est la différence.
IMSoP

6
  • {} est un objet sans propriétés.
  • Comme []suit immédiatement un objet, cela signifie "Accéder à une propriété de ce nom" et non "Créer un tableau"
  • trueest un booléen, mais est utilisé comme nom de propriété, il est donc converti en chaîne ( "true")
  • L'objet n'a pas une propriété appelée true(car il n'a pas de propriétés) donc {}['true']estundefined
  • !undefinedconvertit undefineden booléen ( false)
  • L'opérateur not se transforme falseen true.

2
Dans le cas de {}[true](sans autre contexte), ce {}n'est pas un objet sans propriétés, c'est un bloc de code vide.
IMSoP


4

Jouons un peu plus!

Commençons par nous amuser!:

//----------#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, essayons de comprendre ces comportements fous, un par un:

1) Ici, le {}est analysé comme un bloc de code vide. Sans attribution, négation, regroupement (avec parenthèses) ou toute syntaxe indiquant à l'analyseur qu'il {}s'agit d'un objet littéral, l'hypothèse par défaut est de penser qu'il s'agit simplement d'un bloc vide inutile.

Ceci est une preuve de ce comportement:

{ alert(123) }[true]

Le code ci-dessus affichera l'alerte normalement et sera évalué comme [true], de la même manière {}[true].

Bloquer les instructions sans point-virgule

Une instruction de type bloc n'a pas besoin d'un point-virgule après.

Par exemple:

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

Les deux alertes sont affichées.

Ainsi, nous pouvons voir qu'une instruction de bloc vide, sans point-virgule, est valide et ne fait simplement rien. De cette façon, lorsque vous entrez {}[true]dans la console Developer Tools (ou Firebug), la valeur évaluée sera la valeur de la dernière instruction d'expression . Dans ce cas, la dernière instruction d'expression est[true] .

2) Dans un contexte d'affectation, l'analyseur s'assurera qu'il {}s'agit d'un objet littéral. Lorsque vous faites var a = {}[true], vous supprimez toute ambiguïté et inclinez l'analyseur qui {}n'est pas une instruction de bloc.
Donc, ici, vous essayez d'obtenir une valeur avec une clé à "true"partir d'un objet vide. De toute évidence, il n'y a pas de paire clé-valeur avec ce nom de clé. De cette façon, la variable a n'est pas définie.

Mots réservés comme clés d'objet

ECMAScript 5 permet aux clés d'objet d'être des mots réservés. Ainsi, les clés suivantes sont légales:

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

3) La même explication de l'exemple 1 . Mais ... Si la { b: 12345 }partie est traitée comme une instruction de bloc, quel est le type de l' b: 12345instruction ??

... (?????)

C'est une déclaration d'étiquette , vous l'avez déjà vu auparavant ... Il est utilisé dans les boucles et dans switch. Voici quelques liens intéressants sur les instructions d'étiquette: 1 , (2) [Le meilleur moyen de rompre avec les boucles imbriquées en Javascript? , (3) [ Comment casser les boucles imbriquées en javascript? .

REMARQUE: essayez simplement d'évaluer ceci:

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

Les instructions d'étiquette ne peuvent pas être séparées par l' opérateur virgule , vous devrez les séparer par un point-virgule. Donc ceci est valable:{a: 1; b: 2}

4) Voir les explications des exemples 1 et 3 ...

5) Une fois de plus, nous avons un { b: 12345 }être traité comme un bloc de code, et vous essayez d'accéder à une propriété d'un bloc de code en utilisant la notation par points , et évidemment, ce n'est pas autorisé, et l'analyseur lève une "Unexpected token :"exception.

6) Le code est presque identique à l'exemple ci-dessus, mais en entourant l' { b: 12345 }instruction avec l' opérateur de regroupement d'expression , l'analyseur saura qu'il s'agit d'un objet. De cette façon, vous pourrez accéder à la "b"propriété normalement.

7) Rappelez-vous l'exemple 2 , nous avons une affectation ici, l'analyseur sait que { b: 12345 }c'est un objet.

8) Identique à l'exemple ci-dessus, mais au lieu de la notation par points, nous utilisons ici la notation entre crochets .

9) J'ai déjà dit que cette "identifier: value"syntaxe à l'intérieur d'une instruction de bloc est une étiquette. Mais, vous devez également savoir qu'un nom d'étiquette ne peut pas être un mot-clé réservé (le contraire des noms de propriété d'objet). Lorsque nous avons essayé de définir une étiquette appelée "true", nous avons obtenu un SyntaxError.

10) Encore une fois, nous avons affaire à un objet. Aucun problème avec les mots réservés ici. =)

11) Enfin, nous avons ceci:!{}[true]

Séparons les choses ici:

a) En faisant une négation, nous informons l'analyseur que le {}est un objet .

b) Comme indiqué dans l'exemple 2 , un {}objet n'a pas de propriété appelée true, donc cette expression sera évaluée undefined.

c) Le résultat final est la négation de undefined valeur. Javascript effectue une conversion de type d'implicité et la undefinedvaleur est fausse .

d) Donc, la négation de falseest ...true !

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.