Why is `` (foo) = "bar" 'finished in JavaScript?

In Node.js REPL (also tested in SpiderMonkey) sequence

var foo = null; (foo) = "bar"; 

with foo subsequently equal to "bar" , as opposed to null .

This seems inconsistent because one would think that the brackets at least play out the bar and throw an invalid left in the assignment`.

It is clear that when you do something interesting, it is not inferior to the above path.

 (foo, bar) = 4 (true ? bar : foo) = 4 

According to ECMA-262 on LeftHandExpressions (as far as I can interpret) there are no valid non-terminals that will lead to brackets being accepted.

Is there something I don't see?

+46
javascript ecma
May 14 '17 at 10:42 p.m.
source share
2 answers

It really is. In parentheses, it is permitted to wrap any purpose of the assignment.

The left side of the operation = is LeftHandSideExpression , as you correctly defined. This can be tracked through various levels of confidence ( NewExpression , MemberExpression ) to PrimaryExpression , which in turn can be CoverParenthesizedExpressionAndArrowParameterList :

  ( Expression [In,? Yield] ) 

(in fact, when parsed for the purpose of PrimaryExpression , it is ParenthesizedExpression ).

So, this is really grammar, at least. Is this valid JS syntax really determined by another factor: early static error semantics. Basically, these are prosaic or algorithmic rules, which in some cases make some production extensions invalid (syntax errors). This, for example, allowed the authors to reuse the array and object initialization algorithms for destructuring, but only using certain rules. In the early mistakes for assignment expressions we find

This is an early Reference Error if LeftHandSideExpression is neither ObjectLiteral nor ArrayLiteral, but IsValidSimpleAssignmentTarget for LeftHandSideExpression false .

We can also see this difference in the evaluation of assignment expressions , where simple assignment targets are evaluated using a reference that can be assigned, rather than getting elements of a destructuring pattern, such as object and array literals.

So what does IsValidSimpleAssignmentTarget do for LeftHandSideExpressions? This basically allows you to assign access to properties and prohibits assignments to expression calls. It does not say anything about simple primary expressions that have their own IsValidSimpleAssignmentTarget rule . All he does is extract the expression between parentheses through Covered & shy; Parenthesized & shy; Expression operation , and then check IsValidSimpleAssignmentTarget again. In short: (…) = … valid when … = … valid. It will give true only for Identifiers (as in your example) and properties.

+28
May 15 '17 at 12:12 a.m.
source share

As suggested by @dlatikay , following the existing hunch , the CoveredParenthesizedExpression study provided a better understanding of what is happening here.

Apparently, the reason that no terminal could not be found in spec , to explain why (foo) is acceptable as LeftHandExpression , is surprisingly simple. I assume that you understand how parsers work, and that they work in two separate phases: Lexing and Parse .

What I learned from this little tangent study is that the construction (foo) not technically delivered to the parser, and therefore the engine, as you think.

Consider the following

 var foo = (((bar))); 

As we all know, something like this is completely legal. What for? Well, you can visually just ignore the brackets, while the statement remains in the ideal sense.

Similarly, here is another valid example, even in terms of human readability, as it is explained in parentheses that PEMDAS already makes implicit.

 (3 + ((4 * 5) / 2)) === 3 + 4 * 5 / 2 >> true 

One of the key observations can be easily derived from this, given the understanding of how parsers already work. (remember that Javascript is still parsed ( read: compiled ) and then launched). Literally, these parentheses "indicate the obvious."

So, all that is said, what exactly is happening?

In principle, parentheses (with the exception of functional parameters) are collapsed into the correct groupings of their containing characters. IANAL, but in layman terms, this means that parentheses are only interpreted to direct the parser to group what it reads. If the context of the parentheses is already β€œin order” and therefore does not require any tuning of the emitted AST , then the (machine) code is emitted as if these parentheses did not exist at all.

The parser is more or less lazy, believing that the partners are impudent. (which is wrong in this boundary case)

OK, and where exactly is this happening?

According to 12.2.1.5 Static semantics: IsValidSimpleAssignmentTarget in the specification,

Primary expression: (CoverParenthesizedExpressionAndArrowParameterList)

  • Let expr be CoveredParenthesizedExpression of CoverParenthesizedExpressionAndArrowParameterList.
  • Returns the IsValidSimpleAssignmentTarget expression.

those. If you expect primaryExpression to return everything that is inside the bracket, and use this.

Because of this, in this case it does not convert (foo) to CoveredParenthesizedExpression{ inner: "foo" } , it converts it simply to foo , which preserves the fact that it is an Identifier and, therefore, is syntactically not necessarily lexically valid.

TL; DR

He looked through.

Want a little more insight?

Mark @Bergi's answer .

+15
May 14 '17 at 23:41
source share



All Articles