JavaScript syntax madness - anyone knows the background why this happens - javascript

I am kinda lost on why the following code does what it does the way it does.
For the following expression I would expect 1 as result, since on the right side of a literal we actually have an obj.
Expression:
> { a : 1 }.a
result Chrome:
Syntax error: Unexpect token .
result NodeJS
1
(Another question: why nodejs and chrome differs on this (and on the following))
While the expression itself has a syntax error assigning it to a variable still works.
> var x = { a : 1 }.a; x;
result:
1
Now using eval around the expression does work in Chrome and in NodeJS
> eval({ a : 1 }.a)
result Chrome and Node
1
Now using eval and the string based expression neither platform works
eval("{ a : 1 }.a")
result Chrome:
SyntaxError: Unexpected token >
result NodeJS:
... //REPL waits more code
And finally the parenthesis solves it all but why?
> eval("({ a : 1 }.a)")
Result:
Works everywhere

eval takes a string, so calling eval({ a : 1 }.a) is the same as eval("1").
In statement context, {} delimit a block, not an object literal. You can go in expression context by using () as a grouping operator.
So:
{ a : 1 }.a
Is actually a block, label, numeric literal and dot-access on nothing:
{
a: 1
}
.a
Node REPL actually runs in an expression context to begin with, but that's unusual. They run your code like eval("(" + replInput + ")")

Try ({ a : 1 }).a
This will also work everywhere as the code in round bracket is executed first and then .a of that object is extracted.
`{ a: 1 }`
is just a block and doesn't return anything for the . operator to work on but enclosing it in () does return an object of which .a is extracted

Related

Why does adding parentheses prevent an error?

Why is it when I write {}.key = 0 in the chrome console I get an error:
> {}.key = 0
> Uncaught SyntaxError: Unexpected token .
But when I encapsulate the above expression in parentheses (( )) I get no error:
> ({}.key = 0)
> 0
What exactly is going on here? I would have thought the same error I got in the first scenario still applied to the second?
Image of console output:
{ } are overloaded in JavaScript syntax. They're used for both blocks (of statements) and object literals. The rule is: If a { appears at the start of a statement, it is parsed as a block; otherwise it is an object literal.
In {}.key the { appears at the start of the statement. It parses as
{
// this is an empty block
}
.key // syntax error here
Adding any token before { (such as () makes it parse as an object literal. For example, 42, {}.key = 0 would also work.

Odd behaviour of comparison of object literals

I have searched but could not find logic behind following in JavaScript.
When I type in the Chrome console:
{} == null
it returns
Uncaught SyntaxError: Unexpected token ==
But
{} == {}
and
{} == function(){}
returns false
Why?
I assume you understand why {} == null throws SyntaxError. Long story short it is because { in the begining starting a block statement not an object literal. You could check the answer here
As of why {} == {} this works.
If you check chromium code that evaluates expressions in console. You could find the following (code)
if (/^\s*\{/.test(text) && /\}\s*$/.test(text))
text = '(' + text + ')';
executionContext.evaluate(text, "console", !!useCommandLineAPI, false, false, true, printResult);
This code wraps {} == {} code with parentheses making it a valid expression ({} == {}) comparing two empty object literals. Which evaluates to false because objects are compared by reference.
Node repl has the same behaviour src
if (/^\s*\{/.test(code) && /\}\s*$/.test(code)) {
// It's confusing for `{ a : 1 }` to be interpreted as a block
// statement rather than an object literal. So, we first try
// to wrap it in parentheses, so that it will be interpreted as
// an expression.
code = `(${code.trim()})\n`;
wrappedCmd = true;
}
You can find this in the specs under Statement (art. 12)
12 - Statement
Statement :
Block.
VariableStatement
EmptyStatement
ExpressionStatement
.
.
.
The first applicable rules are either Block or Expression Statement. So we need to look at 12.4.
In 12.4 the specs clearly state that an expression statement cannot start with a {.
though i haven’t yet found what makes example 2 an expression, maybe it’s implementation specific
12.4 Expression Statement
Syntax
ExpressionStatement :
[lookahead ∉ {{, function}] Expression ;
NOTE An ExpressionStatement cannot start with an opening curly brace because that might make it ambiguous with a Block. Also, an ExpressionStatement cannot start with the function keyword because that might make it ambiguous with a FunctionDeclaration.
Semantics
The production ExpressionStatement : [lookahead ∉ {{, function}]Expression; is evaluated as follows:
Let exprRef be the result of evaluating Expression.
Return (normal, GetValue(exprRef), empty).
I would say this is a parsing issue, rather than a logic issue per se.
In Chrome I get the observed behavior.
In IE I get syntax errors whenever I put {} (or, it seems, any object literal) on the LHS of the ==.
In both browsers, putting () around the expression fixed things. Or first assigning the object to a variable
var x = {}
x == null
I would say it seems to me like a bug in the parsing. Whether that is true in an academic sense would take digging through specs and grammars; the practical answer is, there are simple enough work-arounds that the best bet is to not do that.
It’s because JavaScript sucks
The reason it doesn’t work is because
JavaScript takes the type the comparison is taking from the first object.
For instance
3+”1” = 4
But
“3”+1 = 31
The first example does the operation as a number because the first object is a number
The second example sees a string as the first object and treats the operation as concatenation of a string.
For your example
{} is an object but null can’t be converted to an object
It works the other way because
{} can be represented as a null object.

eval is evil, but is it flawed? [duplicate]

This question already has answers here:
Why does JavaScript's eval need parentheses to eval JSON data?
(7 answers)
Closed 7 years ago.
If I run this:
eval('{ear: {"<=": 6}}');
I get an error:
Uncaught SyntaxError: Unexpected token :
Let's create the object manually:
var foo = {};
foo.ear = {};
foo.ear["<="] = 6;
Now, the following code:
JSON.stringify(foo)
Returns the following string:
'{"ear":{"<=":6}}'
The same string as the one I started with (except the white characters, but those are irrelevant), so eval(JSON.stringify(foo)) returns the same syntax error error message. However:
$.parseJSON(JSON.stringify(foo))
is executed correctly. What is the reason of that?
EDIT:
As nnnnnn and Ron Dadon pointed out, the initial string and the result of stringify are different. However, as I pointed out in the question, even the result of stringify used as input for eval will result in the syntax error message.
EDIT2:
Based on the answers and experiments conducted, this function is interesting:
function evalJSON(text) {
return eval("(" + text + ")");
}
Main {} are parsed as block statement.
try to wrap in parenthesis:
eval('({ear: {"<=": 6}})');
In javascript {} can be parsed as a block or an object
examples:
//object
var user = {
name: "John",
age: "32"
};
//block
{
let a = 5;
console.log(a);
}
//object:
var a = {};
console.log({});
return {};
({});
//block:
function(){}
for(k in o){}
{}
Object literal notations need to be evaluated. This happens when you assign a variable:
var a = {ear: {"<=": 6}};
or when you put parentheses around it, a anonymous object:
({ear: {"<=": 6}});
Otherwise curly brackets are parsed as block markers. In your case this means {ear:...} is a label definition, the label is named ear. The next block {"<=": 6} gives you a syntax error because "<=": 6 is invalid syntax.
The same applies if you put this into an eval statement.
It's not flawed. To understand what is happening you need to understand what kind of statements are seen (left to right) by the parser.
A simple way to get into it is to play around with a Javascript AST Visualizer
You will get the same exception with much simpler {"b":4}. It's parsed as "b":4 inside a block. That's not valid javascript. No AST tree for you...
However it's due to an exception inside of a {} statement. That's a BlockStatement. AST tree:
A similar {b:4} would be understood as b:4, a valid js statement - a b label for 4... That's parsed as
Lastly, a ({b:4}) would be understood as an object declaration with a b property equal to 4. That's parsed as
ECMAScript 2015
On Blocks:
Block : { StatementList }
On eval itself:
Eval creates a new Realm, which is parsed (several steps here) as a sequence of Statements ( a StatementList ), which in turn this section has BlockStatement as a first option. This must start with { (see above), so if you wrap it with a bracket (({})) it cannot be a BlockStatement... But if it matches as BlockStatement it must be a BlockStatement.
A side note in section on Expressions:
An ExpressionStatement cannot start with a U+007B (LEFT CURLY BRACKET) because that might make it ambiguous with a Block

Weird JSON parsing behavior in js, "Unexpected token :"

As demonstrated in this jsfiddle, if you have a JS file and you create a JSON object without using it, it behaves differently depending on whether the keys(members) are wrapped in quotes or not.
valid code:{ a: 1};
invalid code: { "a": 1 };
What you will get is an error message (in Chrome, different for FF/IE, but still fails on syntax)
Uncaught SyntaxError: Unexpected token :
but if you use the object in some way, for example: alert({ "a": 1 }); everything is OK again.
Why does this happen?
The statement:
{ a: 1 };
is not an object literal. It's a block statement with one labeled expression in it. It's valid.
This:
{ "a": 1 };
is a syntax error because it's just not parseable. The quoted "a" starts an expression statement inside the block, but then the next token after the string is a colon, and there's no expression form that looks like an expression followed by a colon.
Now:
var x = { "a": 1 };
works because the "{" is not interpreted as the start of a block statement. That statement starts with var, so it's a variable declaration. Within the expression on the right side of the "=" token, the only thing that a "{" can mean is the start of an object literal. Similarly, note that:
({ "a": 1 });
is OK because the opening parenthesis makes the parser expect a nested subexpression, so again the "{" unambiguously means that it's the start of an object literal.
I just realized than when loading the JSON via require and the filename does not end on .json i get this error. Renaming the file to bla.json and it works fine.
This error can popup when doing a jQuery AJAX call using jsonp when jsonp is not necessary. Try switching your data type on your AJAX call if this is the case to normal json
$.ajax({
dataType: 'json', // try using json rather than json p
...
});

What is the behavior of typing {a:1} giving 1, and {a:1, b:2} giving an error in a Javascript console?

The following will show in Firebug or in jsconsole.com or in other Javascript interactive console:
>>> foo = { a : 1, b : 2.2 }
Object { a=1, more...}
>>> foo.a
1
>>> foo.b
2.2
>>> { a : 1, b : 2.2 }
SyntaxError: invalid label { message="invalid label", more...}
>>> { a : 1 }
1
why is the 1 returning for {a : 1} and why is {a : 1, b : 2.2} giving an error? In Ruby, they would come back the same way you defined it.
The second line is giving you a SyntaxError because the { token at the beginning of it causes an ambiguity, the parser treats it as if it were a Block statement, not the start of an object literal.
For example, a valid Block statement:
{ foo: 'bar' }
The above looks like an object literal, but it isn't, because the code is evaluated in statement context.
It will be parsed as a Block, that contains a labelled statement (foo), followed by an expression statement ('bar').
To ensure that you are using the grammar of an object literal, you can wrap it with parentheses (also known as the grouping operator):
({ foo: 'bar' })
The grouping operator can only take Expressions, therefore there is no ambiguity.
See also:
Why the open quote and bracket for eval('(' + jsonString+ ')') when parsing json string
I'm not 100% positive, but what I think is happening is that in the second line you're defining a block, not an object. Thus the parse error comes when the parser reaches the comma, since it expects a semi color. The labels defned are labels, like in a goto or switch statement. I hope this explanation makes any sense.
console do as eval('you input')
eval({....}) --- this will get an error
eval('({....})')---eval string as a function

Categories