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();
Related
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 played around in the Chrome console and wondered why this statement throws a syntax error:
{}.hasOwnProperty('id');
My expectation was the return value false.
A syntax error also occurs in Firefox and IE .
The same works if I use an empty array instead of an empty object.
The same also works if I put braces around:
({}.hasOwnProperty('id'));
There's syntax ambiguity in snippet in question. Curly braces in JavaScript have two meanings: they are used to delimit code blocks, for example:
if (x) {
...
}
and they are used to declare object literals:
var obj = {
prop: "value"
}
Constext is used to differentiate between the two interpretations, and in case of:
{}.hasOwnProperty('id');
braces are resolved to block declaration, so this yields syntax error. On the other hand:
({}.hasOwnProperty('id'));
One cannot declare block inside parentheses, so {} is recognized as object literal in this context.
{} is treated as a statement block rather than an object, as you are expecting. Hence the error.
EDIT: As #Cerbrus mentioned in the comments, ({}).hasOwnProperty('id'); would return false and not an error. This is because when surrounding {} with parenthesis, it is interpreted as an Object.
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.
Can you please tell me the reason for this specific syntax structure
eval('(' + jsonString+ ')')
When parsing json text. Crockford says "The text must be wrapped in parens to avoid tripping on an ambiguity in JavaScript's syntax." here. What does that mean?
Can we avoid it?
The syntax ambiguity to which Crockford refers is that if an open curly brace is not found on expression context, it will be recognized like a block, and not like the start of an object literal.
For example:
{"foo": "bar"} // SyntaxError
Will give you a syntax error, because it will be interpreted as a block, with a string literal "foo", and a unexpected usage of the token :.
On the other hand, the parentheses, formally called the grouping operator, can only evaluate expressions, therefore we will not have any syntax ambiguity because a block can only be expected on a statement context.
({"foo": "bar"})
Edit: #el.pescado makes an interesting question:
Can you explain why eval('{}') is undefined?
ECMAScript describes an internal type to explain the behavior of statements, it's called The Completion Specification Type.
Values of the Completion type are triples of the form of (type, value, target), where type can be normal, break, continue, return, or throw.
value can be any language value or empty, and target any Identifier or empty.
An empty block (the production Block : {}) explicitly returns the following completion:
Return (normal, empty, empty).
The eval function, after executing the code, and exiting the newly created execution context, checks the result completion of the evaluated code, and in the Step 7 we can see that undefined is explicitly returned if the completion type is normal and the completion value is empty:
...
7- If result.type is normal and its completion value is empty, then return the value undefined.
...
An object literal needs to be wrapped in parentheses to properly be evaluated in eval context and other contexts:
eval('{}') is undefined, for example. whereas eval('(' + '{}' + ')' ) evaluates to Object.
If you tried this in the console, for example: {"foo":"bar"} it would throw an invalid label at you. Wrap it in parentheses and it becomes a valid expression.
#el.pescado, the string after executed by eval should be javascript understandable.
i.e if you are assigning the above string to the varible as follows
eval(' var foo1 = {"foo" : "bar"}');
foo1.foo will return bar
so, my assumption is, as there is no statement like that starts with "{" in javascript, it is throwing the error.
Can you please tell me the reason for this specific syntax structure
eval('(' + jsonString+ ')')
When parsing json text. Crockford says "The text must be wrapped in parens to avoid tripping on an ambiguity in JavaScript's syntax." here. What does that mean?
Can we avoid it?
The syntax ambiguity to which Crockford refers is that if an open curly brace is not found on expression context, it will be recognized like a block, and not like the start of an object literal.
For example:
{"foo": "bar"} // SyntaxError
Will give you a syntax error, because it will be interpreted as a block, with a string literal "foo", and a unexpected usage of the token :.
On the other hand, the parentheses, formally called the grouping operator, can only evaluate expressions, therefore we will not have any syntax ambiguity because a block can only be expected on a statement context.
({"foo": "bar"})
Edit: #el.pescado makes an interesting question:
Can you explain why eval('{}') is undefined?
ECMAScript describes an internal type to explain the behavior of statements, it's called The Completion Specification Type.
Values of the Completion type are triples of the form of (type, value, target), where type can be normal, break, continue, return, or throw.
value can be any language value or empty, and target any Identifier or empty.
An empty block (the production Block : {}) explicitly returns the following completion:
Return (normal, empty, empty).
The eval function, after executing the code, and exiting the newly created execution context, checks the result completion of the evaluated code, and in the Step 7 we can see that undefined is explicitly returned if the completion type is normal and the completion value is empty:
...
7- If result.type is normal and its completion value is empty, then return the value undefined.
...
An object literal needs to be wrapped in parentheses to properly be evaluated in eval context and other contexts:
eval('{}') is undefined, for example. whereas eval('(' + '{}' + ')' ) evaluates to Object.
If you tried this in the console, for example: {"foo":"bar"} it would throw an invalid label at you. Wrap it in parentheses and it becomes a valid expression.
#el.pescado, the string after executed by eval should be javascript understandable.
i.e if you are assigning the above string to the varible as follows
eval(' var foo1 = {"foo" : "bar"}');
foo1.foo will return bar
so, my assumption is, as there is no statement like that starts with "{" in javascript, it is throwing the error.