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.
Related
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.
when I try it in chrome dev tools, it shows
Uncaught SyntaxError: Unexpected token .
However if when it assign to a:
var a={}.toString();
a //[object Object]
what caused the difference?
what caused the difference?
The state the parser is in. By default, the parser is in a state where it expects a statement. So in your example in the console, the { looks like the opening of a block to it, not the beginning of an object initializer. (You can also give it an expression at that point, because JavaScript has the concept of the ExpressionStatement, which is a statement consisting entirely of an expression.)
But in your var a={}.toString(); code, the {}.toString() appears on the right-hand side of an assigment, where the parser is expecting an expression, not a statement. So the { starts an object initializer.
If you do something to make the parser expect an expression instead, it'll work in the console too:
({}).toString(); // "[object Object]"
or
+{}.toString(); // NaN, because the `+` tries to turn `[object Object]` into a number and fails
When you aren't in expression context (as is triggered by being on the right hand side of an assignment, for instance) then {} is a block statement and not an object literal.
{} is captured first and interpreted as a block, which has no .toString method to call.
If you wrap the object literal in parens, like ({}).toString(), then it works as expected.
This is because of the parsing rule priorities and the {} tokens being overloaded to be both object and block delimiters.
The parser is interpreting the {} as a code block. You can make it parse correctly by surrounding the braces with parentheses:
({}).toString();
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.