22

I was reading the answer to this question (about the "wat" video) and it said:

  1. {}+[]
    This is interpreted as an empty block of code, unary plus and empty array. First part does nothing, array is converted to a comma-separated string of it's elements (empty string for empty array), then to a number (empty string is converted to 0), hence 0.

I am currently learning JS from "The Definitive Guide" so I try to really understand things like that.

My question is, when does JS decide to interpret {} as an empty block of code, instead of an empty object?

Also, there are some inconsistencies between Node.js and Firebug which I would like to understand.

Firebug:

Firebug console output for <code>{}[]</code> and <code>({}[])</code>

Node.js:

Node.js output for <code>{}[]</code> and <code>({}[])</code>

Community
  • 1
  • 1
Ella Sharakanski
  • 2,683
  • 3
  • 27
  • 47
  • 1
    I don't think this is a duplicate, in that it explicitly *references* the other question and asks about a detail. – Pointy Oct 13 '14 at 19:35
  • @Pointy - It references a different question. The linked one clearly explains the behavior. As noted in the duplicate, [] and {} are both converted to their primitive values. `.join` on an empty array is an empty string. toString.call({}) is "[Object object]". Expressions cause concatenation, for example ("1"+0) = 10 or "[Object object]" + "" = "[Object object]" – Travis J Oct 13 '14 at 19:44
  • 1
    @TravisJ I agree that it's a matter of opinion over whether it's really a duplicate. I don't have a big problem with duplicates, but I understand that others do so if you feel strongly about it go ahead and close it and I won't touch it :) – Pointy Oct 13 '14 at 19:48
  • @TravisJ Also I didn't realize I had god-like powers to reopen without a vote :) – Pointy Oct 13 '14 at 19:49

2 Answers2

26

Let's look at the language grammar, shall we? Section 12, Statements:

Statement :
    Block
    VariableStatement
    EmptyStatement
    ExpressionStatement
    ...lots of other stuff...

That's a very fancy way of saying that a statement can be a block, a variable statement, an empty statement, an expression statement, or lots of other stuff. Notice that the first option there is a 'Block':

Block :
    { StatementList(opt) }

StatementList :
    Statement
    StatementList Statement

Which is again, a fancy way of saying that a block is a {, optionally followed by a bunch of statements, followed by a }.

And that's what you see in your example: Before the JavaScript parser thinks that what you have could be an object literal (which is defined somewhere under ExpressionStatement, the 4th thing a 'Statement' could be), it first thinks that you have a 'Block'.

Edit: If you want, you can see it live in a JavaScript engine's source code:

Regarding your second question, that's been covered to great detail on this question. To summarise in a sentence: Node.js treats your input as if it were an expression (thus it can't be a 'Block'), while Firebug/Chrome dev tools treat it like a 'Statement'.

Community
  • 1
  • 1
Zirak
  • 38,920
  • 13
  • 81
  • 92
  • I don't think it's correct that the Node REPL treats the input like an expression - you can type in a `for` loop in the REPL, for example. – Pointy Oct 13 '14 at 19:43
  • That's why the summary is in a sentence. Benjamin Gruenbaum's answer goes into more detail: the REPL first tries to execute it as an expression, and if a SyntaxError is thrown, it goes to straightforward `eval`. Why? *shrug* – Zirak Oct 13 '14 at 19:45
  • OK I see now - Node *explicitly* checks for the case of an object literal (leading `{`). I don't think I'd do that (principal of least surprise) but whatever. – Pointy Oct 13 '14 at 19:47
  • Thank you for the interesting references! I still don't understand why the semicolon in the Node.js example matters. – Ella Sharakanski Oct 13 '14 at 21:50
  • Well, a semicolon isn't allowed in an expression, so it'd fail to be evaluated as an expression and instead be evaluated as a statement. – Zirak Oct 13 '14 at 22:13
2

When the first token in a new statement is {, then {} is interpreted as an empty block.

(Actually of course when { appears after the header clause of something like if or while, then {} is an empty block too, but that's not the interesting case.)

Thus in any other context, like say an argument to a function:

foo({});

the {} is interpreted as an empty object literal.

This situation is similar to the way in which the function keyword is treated differently when it's the first thing in a statement. The syntax has ambiguity, and the parser solves the problem with fixed rules.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • How do you explain the first Node.js example? Thanks! – Ella Sharakanski Oct 13 '14 at 19:32
  • @EllaShar I'm not too familiar with Node, but it basically just passes the string you type to `eval()`, but it does it in some way that alters the syntactic context. If you type `;{}+[]` in Node, then you get `0` like in Firebug. – Pointy Oct 13 '14 at 19:34