I was reading the answer to this question (about the "wat" video) and it said:
{}+[]
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:
Node.js:
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:
In V8, Chrome's JavaScript engine, we go into Parser::ParseStatement. The first thing it checks is whether we're on a {, and if it does, parses as block.
In SpiderMonkey, Firefox's JavaScript engine, we go from Parser::statement to again see the first check being against a { and parsing it as a block statement.
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'.
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.
Related
I noticed today that Chrome 49 no longer outputs NaN when you type {}+{} into the console. Instead it outputs the string [object Object][object Object].
Why is this? Did the language change?
Chrome devtools now automatically wrap everything that begins with { and ends with } in an implicit pair of parentheses (see code), to force its evaluation as an expression. That way, {} creates an empty object now. You can see this if you go back through the history (↑), the previous line will be contained in (…).
Why? I don't know, but I could guess it reduces confusion for newbies that don't know of the block-vs-object-literal thing, and it's also more helpful if you just want to evaluate an expression.
And in fact that's the reasoning, as discussed in bug 499864. Pure convenience. And because node REPL had it as well (see code).
If you hit the up arrow after checking this, you'll notice that instead of {} + {} it displays ({} + {}), which results in "[object Object][object Object]".
In comparison, in Firefox, {} + {} still displays NaN, but if you do ({} + {}) it also displays "[object Object][object Object]".
So, it looks like Chrome is adding the surrounding parenthesis automatically when it sees this operation.
As of Chrome 54 with regards to the console:
Unfortunately, I added the Clippy quote myself. The console gives no information about what it has done for you.
The new rules are incredibly simple saving us the trouble of laboriously typing these 2 difficult charcters o= or 0, before pasting Object Literals into the console:
If you have code that starts with: optional whitespace,(no comments permitted) followed by a {;
and that code could be interpreted as an object;
and that object is followed by no other code, unless:
the code after the first object is a binary operator,
then there can be as many operations as you like including groupings
provided the final operator has an Object literal in the right hand position;
and that final Object has not been grouped in parens
and that code is not terminated with a semicolon
and there are no comments following the code (internal comments are permitted so long as they are not in the initial or final position)
then and only then will your JavaScript (which may or may not actually be valid code) will be re-intrepted as a valid Object. You will not be informed that your code has been reinterpreted.
{wat:1}),({wat:2} Is finally an error again.
{let i=0;var increment=_=>i++} is correctly allowed, finally, which is quite a nice way of doing closures.
However, the following is incorrectly an object, this is just as a convenience as mentioned by #Bergi, it interprets JS wrong to help you! The spec says it is a block with a labeled statement "foo" with a literal 1 that is not assigned to anything.
{foo:1}
The above should be the same as
if(1) {
foo: 1
}
The following is treated correctly as a block... because it has a comment in front of it!
//magic comment
{foo:1}
So is this:
{foo:1}
//also magic
This is an Object:
{foo:
//not so magic comment
1}
This is an error
//not so magic comment
{foo:1}.foo
So is this:
{foo:1}.foo
This is fine:
1..wat
undefined
so is this:
['foo'][0]
The next one is correctly interpreted as an object whacked into the expression position with a 0, which is generally how we unambiguously ensure we have an expression instead of a statement.
0,{foo:1}.foo
I don't get why they wrap the value in parens. JS has some ridiculous design decisions, but trying to make it behave nicer in this one situation isn't really an option, the console needs to run JS correctly, and we need to be confident that chrome isn't just guessing that it thinks we really meant it to do something else.
If you don't like comma operators you can use assignment
x = {foo:1}.foo
Because as it stands
{} + {} + {}
"[object Object][object Object][object Object]"
;{} + {} + {}
"NaN[object Object]"
Crazy and consistent I can deal with... crazy and inconsistent no thank you!
acorn version: 5.2.1
my code:
The error disappear until like this:
It's there any way to resolve this problem?
TL;DR surround the object with braces (), See it here
This happens because in JavaScript, {} not in expression context is parsed to be a block, the "a": inside would be a label (if it weren't invalid) and the {} would be another, internal block. See it here
By wrapping it in braces, you tell the interpreter that it's actually an expression, and a block can't be in an expression, thus the whole thing must be an object. See it here
The reason it works in your second example is that only expressions can be found on the right side of an assignment, which brings it back to expression context.
I was reading the answer to this question (about the "wat" video) and it said:
{}+[]
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:
Node.js:
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:
In V8, Chrome's JavaScript engine, we go into Parser::ParseStatement. The first thing it checks is whether we're on a {, and if it does, parses as block.
In SpiderMonkey, Firefox's JavaScript engine, we go from Parser::statement to again see the first check being against a { and parsing it as a block statement.
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'.
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.
I was reading the answer to this question (about the "wat" video) and it said:
{}+[]
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:
Node.js:
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:
In V8, Chrome's JavaScript engine, we go into Parser::ParseStatement. The first thing it checks is whether we're on a {, and if it does, parses as block.
In SpiderMonkey, Firefox's JavaScript engine, we go from Parser::statement to again see the first check being against a { and parsing it as a block statement.
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'.
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.
I was expecting JavaScript to reject objects with duplicated properties as invalid but it accepts them in some cases.
{"a":4,"a":5} results in an SyntaxError at least in Firefox and Chrome which seems obvious due to the property a being defined twice.
However ({"a":4,"a":5}) evaluates just fine and results in an object {"a":5} in both Firefox and Chrome.
Why is the expression with the parenthesis accepted?
Summing up the responses: The first example is simply not the construction of an object but a block of labeled statements. Duplicated properities in objects are perfectly valid in which case the last definition wins.
Thanks a lot for your answers!
It is perfectly legal in ECMAScript 3 to declare duplicate properties in an object literal; the SyntaxError you get probably comes from the fact that you used an object literal as a statement, which is not possible due to the confusion with block statements ({ doSomething(); }).
If you want this to be reported as an error, you may want to switch to ECMAScript 5's strict mode: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode.
What you state has no problem if you assign it to a variable, if you don't however, you get the error you mention. Which makes all the difference from a syntax point of view.
When you wrap any structure in parens you are causing that syntax to be evaluated as an expression, the result of which is stored as a temporary variable. The error I get when not doing so in Firefox is unexpected label or invalid label, so it seems without assignment, or parens, this object construction is not treated as an object construction - instead it is treated as a block with multiple label statements that are defined illegally:
{
a: function(){
alert('a');
},
b: function(){
alert('b');
}
}
The above should be totally acceptable as an object, however you get a similar error if you evaluate it without assinging it to some form of variable, or evaluating it with parens. Put simply the duplication of the attribute name is not causing the error :)
Basically imagine your first example, but like this:
function (){
"a": 4,
"b": 5
}
That is roughly how these browsers are treating it, which is now obviously illegal javascript syntax... whereas it wasn't so obvious before.
In the first notation (parentheses-less) the javascript syntax is ambiguous. From ecmascript specification:
An ExpressionStatement cannot start with an opening curly brace
because that might make it ambiguous with a Block.
A block basically evaluates all the statements inside, equivalent to evaluating "a":4,"a":5 which is not valid JS and, in fact, returns the same SyntaxError Unexpected token :
Wrapping that code in parentheses (or, rather, a grouping operator) removes that ambiguity since an assignment expression cannot be followed by a block statement:
var test = {"a":"a","a":"b"}; //test.a === "b"
Furthermore this ambiguity can be removed by any operator or expression that cannot be used with a block statement. A practical scenario hardly comes to mind, maybe if you wanted to return an object literal as part of a conditional operator?
//this *could* come out of a JS minifier:
return x ? (foo(),{"a":"b"}) : (bar(), {"b":"a"});
Why should it not be accepted? You're simply overwriting the values. I think it's rather a feature, than an error. And it works fine for me on various browsers: http://jsbin.com/oculon/1/edit
It's like writing
var a;
a = 4;
a = 5;
alert(a);
it's not Error you just overwrite value with another
I'm guessing (though not certain) that this evaluates as an error because of the difference between the way Firefox and Chrome's JS parsers treat statements and expressions. So because it's wrapped in parentheses the second time, it's considered an expression. Since it's looking for less information in an expression, it can ignore erroneous values. You'll see that if you do...
var a = {'a': 5, 'a': 4};
console.log(a);
It works fine! And also notice that here it's in the right hand side of the statement, giving a hint that it's an expression.