131

{}[true] is [true] and ![true] should be false.

So why does !{}[true] evaluate to true?

Ionică Bizău
  • 109,027
  • 88
  • 289
  • 474
user2430508
  • 1,011
  • 1
  • 7
  • 6
  • 30
    `var o = {}; o[true] === undefined`. – azz Oct 31 '13 at 09:39
  • 2
    The explanation here will likely be very similar to the oddities discussed on [this previous question](http://stackoverflow.com/q/9032856/157957) – IMSoP Oct 31 '13 at 09:40
  • 45
    "Because Javascript is silly" is probably not the answer you're looking for. – georg Oct 31 '13 at 09:44
  • `{}[true] === undefined`, try running this in node console, It's like: `{} + {} === NaN` (in Chrome console) and `{} + {} === "[object Object][object Object]" (in nodejs)`. Explanation [here](http://stackoverflow.com/q/17268468/1420197) – Ionică Bizău Oct 31 '13 at 09:47
  • 2
    As mentioned, if you're are getting `{}[true] === [true]` from a console, that is because it is treating `{}` as an empty code block, not an object. – azz Oct 31 '13 at 09:48
  • 3
    if it can help, try to compare `{}` and `({})` in your console(or `{}[true]` and `({})[true]`). Also, as nobody mentionned it, object[true] is evaluated to object["true"]. – BiAiB Oct 31 '13 at 12:28
  • Also `({}[true])` evaluates to `undefined`. I'd really say that this is an implementation quirk of consoles having to parse the code in a different context... – Izkata Oct 31 '13 at 15:40
  • I'm surprised no one has noted yet that `{}[false] ? true : false` returns true – Ryan Amos Nov 02 '13 at 02:05
  • @RyanAmos whatever you put inside the indexer it will return undefined since you are trying to get a property from a plain empty object – Áxel Costas Pena Nov 05 '13 at 22:03
  • I'm all for deepening the understanding of JavaScript, but do you plan on writing code like this or have you seen this code in production? – Razor Nov 08 '13 at 04:52

10 Answers10

173

I believe that's because plain {}[true] is parsed as an empty statement block (not an object literal) followed by an array containing true, which is true.

On the other hand, applying the ! operator makes the parser interpret {} as an object literal, so the following {}[true] becomes a member access that returns undefined, and !{}[true] is indeed true (as !undefined is true).

Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
  • 25
    The fact that !undefined is true, on the other hand, is still inexcusable. – evilcandybag Oct 31 '13 at 13:21
  • 87
    @evilcandybag: It's absolutely not. `undefined` is falsy (something that we rely on often -- `if (obj.maybeExists) ...`), so it makes perfect logical sense that `!undefined` is true. – josh3736 Oct 31 '13 at 14:24
  • 8
    @Josh, I think evilcandybag would prefer a behavior akin to `null` in some languages, with `!undefined` being equal to `undefined`. That's not the case in Javascript, though. – Frédéric Hamidi Oct 31 '13 at 14:28
  • 1
    This seems to be environment-specific. Assigning `{}[true]` to a variable, `alert`ing it, even just sticking it inside parens like `({}[true])` causes it to evaluate to `undefined` instead of `[true]`. So it seems like this is a result you can only get inside a console due to a quirk in the parser/lexer... – Izkata Oct 31 '13 at 15:44
  • 6
    @evilcandybag: it only makes logical sense to say that something that is `not undefined` (`!undefined`) must therefore be defined. If something is defined then it's usually interpreted as `true`. – OozeMeister Oct 31 '13 at 16:50
  • 2
    @OozeMeister That's ridiculous. `a=false`. `a` is defined, well defined, and is false. Let's say a and b are both undefined, but we know a != b. Since b is undefined, a must be true? But since a is undefined b must be true? But that can't be, because a != b. You arrive at total contradictions if you just assume things about something undefined. – Cruncher Oct 31 '13 at 17:19
  • 7
    @Cruncher If a is undefined, and b is undefined, how can we possibly know that a != b? Particularly when the only known characteristic of the two variables is exactly the same. – LJ2 Oct 31 '13 at 18:15
  • 1
    Javascript uses `undefined` in the way that most languages use `null`. In Javascript, `null` is an actual object (and therefore evaluates `true`, sometimes). It's crazy. – fluffy Nov 01 '13 at 06:22
  • 2
    @fluffy, null is *definitely not* an object. [link](http://stackoverflow.com/questions/3662952/is-null-an-object-in-javascript) [link](http://stackoverflow.com/questions/18808226/why-is-typeof-null-object/18808270) [link](http://stackoverflow.com/questions/801032/why-is-null-an-object-and-whats-the-difference-between-null-and-undefined). Where can you get it to evaluate to true? – Áxel Costas Pena Nov 05 '13 at 22:08
  • @FrédéricHamidi: how the hell did you come to that solution? I mean, it's as counterintuitive as it gets (`{}[true]` being parsed as that). – Erik Kaplun Nov 05 '13 at 22:24
  • 1
    @ÁxelCostasPena Oops, my bad. Most of my familiarity with JS is from the interpreter binding side (i.e. working with V8 and JavaScriptCore), and on the native side, the null non-object-with-weird-typing behaves differently than a null value. – fluffy Nov 05 '13 at 23:26
  • @Erik, deduction, mostly. Clearly `{}` wasn't interpreted as an object literal since member access did not take place and only the subsequent array was evaluated, as if these tokens were ignored. Moreover, `{ foo: "bar" }[true]` also resulted in anything but the array being ignored, and `{ foo: "bar", baz: "quux" }[true]` *resulted in a syntax error* (missing semicolon before statement). From there I only had to find which construct would be valid but ignored in the first two cases and invalid in the third, and the only one matching these requirements was the statement block. – Frédéric Hamidi Nov 06 '13 at 08:57
  • OK, so basically the syntax of JS doesn't support accessing of contents of object literals like that :) – Erik Kaplun Nov 06 '13 at 14:53
  • @evilcandybag, and what should `!undefined` be? (The real travesty is that `undefined` exists at all.) – Paul Draper May 09 '14 at 17:18
  • 1
    @PaulDraper either undefined, as any operation on undefined is undefined, or a runtime error as performing operations on undefined values should not really be possible. – evilcandybag May 09 '14 at 17:47
44

Because {}[true] does not return true, but undefined, and undefined is evaluated as false:

http://jsfiddle.net/67GEu/

'use strict';
var b = {}[true];
alert(b); // undefined
b = !{}[true];
alert(b); // true
dooxe
  • 1,481
  • 12
  • 17
  • 21
    If you evaluate `{}[true]` in a console, you get `[true]`, because the `{}` is interpreted as an empty code block, not an object. It's all about the context and the ambiguity of `{}`. – IMSoP Oct 31 '13 at 09:45
  • @VitaliyPetrychuk Nope, it's standard behaviour. See [this previous question](http://stackoverflow.com/q/9032856/157957) for lots more examples of the oddities produced by this ambiguity. – IMSoP Oct 31 '13 at 09:47
  • 1
    @IMSoP but why does `{key:"value"}[1,2,3];` also evaluate to `[1,2,3]` ? – t.niese Oct 31 '13 at 09:52
  • 3
    @t.niese, because it's parsed as a statement block containing a label (`key:`) and a string literal (`"value"`), followed by an array. The parser still does not see an object literal. – Frédéric Hamidi Oct 31 '13 at 09:54
  • 1
    @FrédéricHamidi ah yes, thats it. I repressed labels ^^ – t.niese Oct 31 '13 at 09:56
  • @IMSoP: I think it doesn't : http://jsfiddle.net/MxASB/ The fact is that a coma separated values like `1,2,3` seems to be evaluated a 3 before using `[]` operator. And 3 is not a key of object `{key:'value'}`, so it returns `undefined`. But if you do : `{key:'value'}[1, 2, 'key']`, it returns 'value'. – dooxe Oct 31 '13 at 10:02
  • @dooxe, `{key:'value'}[1, 2, 'key']` evaluates to `[1, 2, 'key']` for me (on Firefox). The assignment to `b` (and the call to `log()`) in your fiddle changes the parser's behavior and allows it to see an object literal instead of a statement block, which is why `undefined` is logged and `value` is printed. – Frédéric Hamidi Oct 31 '13 at 10:08
  • Yes you're right, I have just tried it. It's a very weird thing that evaluating it in the console gives an array, and it is not possible to obtain it in a javascript ... (i'm using firefox too) – dooxe Oct 31 '13 at 10:12
  • 1
    @dooxe Read the other answers; it's all about the context in which it is interpreted. If you wrap it in `alert()` or `console.log()`, or assign it to a variable, you are changing the context, which is why it doesn't behave the same way as typed on its own in a console. – IMSoP Oct 31 '13 at 10:14
  • @IMSoP Yes, I know it ;) What I wanted to say is that if there is no way to get an array value from `{key:"value"}[1,2,3]` using a programming context (assignation, function argument) what would be the usefulness of it ? – dooxe Oct 31 '13 at 10:22
  • @dooxe I don't think anyone ever said it was *useful* - just very confusing when it does happen! – IMSoP Oct 31 '13 at 10:24
  • 1
    For those wondering what a statement block is, here is a decent explanation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block ... note that chrome treats it as a statement block due to the object container not being assigned to a variable. – marksyzm Oct 31 '13 at 16:42
27

Because

{}[true]

evaluates to undefined, and !undefined is true.

From @schlingel:

true is used as key and {} as hash map. There doesn't exist an property with the key true so it returns undefined. Not undefined is true, as expected.

Console session (Node.js [0.10.17]):

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

However, in the Google Chrome console:

> !{}[true]
true

So, no inconsistencies. You're probably using an old version of the JavaScript VM. For those who need further evidence:

Enter image description here

UPDATE

With Firefox, it also evaluates to true:

Enter image description here

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Games Brainiac
  • 80,178
  • 33
  • 141
  • 199
23

The reason for the confusion is down to a misunderstanding of your first assertion:

{}[true] is [true]

What you're seeing when you run it is the result of an ambiguity. Javascript has a defined set of rules as to how to handle ambiguities like this, and in this case, it breaks what you see as a signle statement down into two separate statements.

So Javascript sees the above code as two separate statements: Firstly, there is a {}, and then there is an entirely separate [true]. The second statement is what is giving you the result [true]. The first statement {} is effetively entirely ignored.

You can prove this by trying the following:

({}[true])

ie wrapping the whole thing in brackets to force the interpreter to read it as a single statement.

Now you'll see that the actual value of your statement is undefined. (this will also help us later to understand the next part)

Now we know that the initial part of your question is a red herring, so let's move onto the final part of the question:

So why does !{}[true] evaluate to true?

Here, we have the same statement, but with a ! appended to the front of it.

In this case, Javascript's rules tell it to evaluates the entire thing as a single statement.

Refer back to what happened when we wrapped the earlier statement in brackets; we got undefined. This time, we are effectively doing the same thing, but putting a ! in front of it. So your code can be simplified as !undefined, which is true.

Hopefully that explains it a bit.

It is a complex beast, but the lesson to learn here is to use brackets around your statements when evaluating them in the console, to avoid spurious results like this.

Spudley
  • 166,037
  • 39
  • 233
  • 307
  • 2
    I don't think `{}[true]` is *invalid* exactly, just *ambiguous*. It can be interpreted as either "empty code block followed by array literal" or "object literal with no properties, of which a property is being accessed". I don't know if the first is technically a case of ASI (many languages wouldn't put a semi-colon there anyway) but it's the context-sensitive interpretation that's the heart of the issue. – IMSoP Oct 31 '13 at 09:55
  • @IMSoP - I had already edited the answer before you posted the comment. :) – Spudley Oct 31 '13 at 09:58
  • 1
    It still says "{}[true] is not actually valid at all" right at the beginning of the answer. – IMSoP Oct 31 '13 at 10:00
  • Also, OP didn't say "`{}[true]` is `true`" they said "`{}[true]` is `[true]`", which is one of the two valid interpretations of the ambiguous statement. – IMSoP Oct 31 '13 at 10:02
14

{}[true] is undefined. To find that write this:

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

or simply:

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

We know that !undefined is true.


From @Benjamin Gruenbaum's answer:

Chrome dveloper tools does the following:

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

So basically, it performs a call on the object with the expression. The expression being:

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

So, as you can see, the expression is being evaluted directly, without the wrapping parenthesis.

More information can be found in this question.

Community
  • 1
  • 1
Ionică Bizău
  • 109,027
  • 88
  • 289
  • 474
10

The answers here are good, here's a breakdown in pseudo-code:

  • {}['whatever'] = empty block, NewArray('whatever') = NewArray('whatever')
  • {}[true] = empty block, NewArray(true) = NewArray(true)
  • !{}['whatever'] = LogicalNOT(convertToBool(NewObject.whatever)) = LogicalNOT(convertToBool(undefined)) = LogicalNOT(false) = true
  • ({}['whatever']) = Grouping(NewObject.whatever) = Grouping(undefined) = undefined
Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
Ben Lesh
  • 107,825
  • 47
  • 247
  • 232
8

This happens because {} in your meaning is not literal presentation of Object, but empty scope ( or empty code block ):

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

It just evaluates code inside scope and then shows you your array.

And from your

!{}[true]

Just converts to int this scope and return same array true. There is no bool checks in this code.

And if you will try to check result from {}[true] you will get your false:

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

As there is no more any scope.

So ! in your question do the same as:

!function() {
   //...
}
antyrat
  • 27,479
  • 9
  • 75
  • 76
  • This is more easily seen if you do `var x = {}; x[true]`. – Chris Hayes Oct 31 '13 at 09:43
  • 1
    I'm not sure what you mean by "converts to int this scope"; I think with the leading `!` it *is* interpreted as an empty object, not scope, and this is the discrepancy. – IMSoP Oct 31 '13 at 09:43
6
  • {} is an object with no properties.
  • Since [] immediately follows an object, it means "Access a property of this name" and not "Create an array"
  • true is a boolean, but is being used as an property name so it is cast to a string ("true")
  • The object does not have a property called true (since it has no properties) so {}['true'] is undefined
  • !undefined casts undefined to a boolean (false)
  • The not operator turns false into true.
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • 2
    In the case of `{}[true]` (with no other context), `{}` is *not* an object with no properties, it's an empty code block. – IMSoP Oct 31 '13 at 09:46
4

You're not reversing the value of it.

![true] != [!true]

Check this out: Why is !true ? 'false' : 'true' returning 'true'?

Community
  • 1
  • 1
Dropout
  • 13,653
  • 10
  • 56
  • 109
4

Let's Play a Little More!

First, let's have some fun!:

//----------#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, let's try to understand these crazy behaviors, one by one:

1) Here, the {} is parsed as an empty code block. Without an assign, negation, grouping (with parentheses) or any syntax which indicates to the parser that this {} is an object literal, the default assumption is to think it is simply a useless empty block.

This is a proof of this behavior:

{ alert(123) }[true]

The code above will show the alert normally, and will be evaluated as [true], in the same way {}[true] is.

Block Statements Without Semicolons

A block-type statement doesn't need a semicolon after it.

For instance:

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

Both alerts are shown.

So, we can see that an empty block statement, without a semicolon, is valid and simply does nothing. This way, when you enter {}[true] in the Developer Tools (or Firebug) Console, the evaluated value will be the value of the last expression statement. In this case, the last expression statement is [true].

2) In an assignment context, the parser will make sure that {} is an object literal. When you do var a = {}[true], you remove any ambiguity and tip the parser off that {} is not a block statement.
So, here, you're trying to get a value with a key "true" from an empty object. Obviously, there's no key-value pair with this key name. This way, the a variable is undefined.

Reserved words as Object keys

ECMAScript 5 allows object keys to be reserved words. So, the following keys are legal:

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

3) The same explanation of example 1. But... If the { b: 12345 } part is treated as a block statement, what's the type of the b: 12345 statement??

... (?????)

It's a label statement, you already saw it before... It's used in loops and in switch. Here are a few interesting links about label statements: 1, (2)[Best way to break from nested loops in Javascript?, (3)[How to break nested loops in javascript?.

NOTE: Just try to evaluate this:

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

Label statements can't be separeted by the comma operator, you would need to separate them with a semicolon. So this is valid: {a: 1; b: 2}

4) See the explanations for the examples 1 and 3...

5) One more time, we have a { b: 12345 } being treated as a code block, and you're trying to access a property of a code block by using the dot notation, and obviously, this is not allowed, and the parser throws an "Unexpected token :" exception.

6) The code is almost identical to the above example, but by surrounding the { b: 12345 } statement with the expression grouping operator, the parser will know that is an object. This way, you'll be able to access the "b" property normally.

7) Remember the example 2, we have an assignment here, the parser knows that { b: 12345 } is an object.

8) Identical to the above example, but instead of the dot notation, here we're using the bracket notation.

9) I already said that this "identifier: value" syntax inside a block statement is a label. But, you also have to know that a label name can't be a reserved keyword (the opposite of object property names). When we tried to define a label called "true", we got a SyntaxError.

10) Again, we're dealing with an object. No problems using reserved words here. =)

11) Finally, we have this: !{}[true]

Let's separate the things here:

a) By doing a negation, we're informing to the parser that the {} is an object.

b) As shown in the example 2, a {} object doesn't have a property called true, so this expression will evaluate to undefined.

c) The final result is the negation of undefined value. Javascript performs implicity type conversion, and undefined value is falsy.

d) So, the negation of false is... true!

Community
  • 1
  • 1
Alcides Queiroz
  • 9,456
  • 3
  • 28
  • 43