What is the exact parsing precedence of arrow function (fat arrow =>) in Javascript?

I came across an example from eslint documentation on arrow function:

// The intent is not clear
var x = a => 1 ? 2 : 3;

So I researched a bit regarding the precedence of arrow functions. It seems that => is not considered an operator, as it cannot be found on the table of operator precedence on MDN. And from the page arrow functions, it says that

arrow functions have special parsing rules that interact differently with operator precedence compared to regular functions.

But it does not further elaborate on the special parsing rules. So my question is, what is the rule of precedence regarding arrow functions?

Based on my test, it seems that its precedence is higher than an assignment, but lower than the conditional(ternary) operator?

var x = 0, a = 5;
console.log(x = a => 1 ? 2 : 3);
// same as x = (a => (1 ? 2 : 3))

But I’m not sure if this is consistent on different browsers and platforms. Can anyone give a definitive answer to this behavior?

As you say, => is not an operator. Arrow functions are primary syntax.

The rules for them are defined in the specification, starting with the ArrowFunction production. ArrowFunction is defined as ArrowParameters followed by => followed by the misleadingly-named ConciseBody. ConciseBody has two forms. You’re asking about the form using ExpressionBody, which is where the first non-whitespace token after => isn’t {. If there were an opening curly brace there it would denote the opening of a block called a FunctionBody instead.

The ExpressionBody definition is quite simple: it’s an AssignmentExpression.

Which takes us into very familiar territory, because AssignmentExpression is the syntax for the right-hand side of an assignment (or variable initializer), entries in an array initializer, the value part of a property initializer, an argument to a function, etc. So whatever follows the => in a concise body has the same parsing rules as what we put where AssignmentExpression is below:

x = AssignmentExpression;
y = AssignmentExpression, z = AssignmentExpression;
a1 = [AssignmentExpression];
a2 = [AssignmentExpression, AssignmentExpression];
o1 = {foo: AssignmentExpression};
o2 = {foo: AssignmentExpression, bar: AssignmentExpression};
doSomething(AssignmentExpression, AssignmentExpression);

Just for detail, an AssignmentExpression is any of:

(You may wonder, as I did, how the y in x = y can match AssignmentExpression given the definition of it above, since y is clearly an Identifier and none of those looks like it will lead to the Identifier production. This is where specs can be really hard to read. It turns out that you get there if you keep going long enough. The path is (deep breath): AssignmentExpression →
ConditionalExpression →
LogicalORExpression →
LogicalANDExpression →
BitwiseORExpression →
BitwiseXORExpression →
BitwiseANDExpression →
EqualityExpression →
RelationalExpression →
ShiftExpression →
AdditiveExpression →
MultiplicativeExpression →
ExponentiationExpression →
UnaryExpression →
UpdateExpression →
LeftHandSideExpression →
NewExpression →
MemberExpression →
PrimaryExpression →
IdentifierReference →
Identifier — whew! [mops brow]. Thank you Oriol!)

Read More:   to remove first and last element in array

It does not further elaborate on the special parsing rules.

They’re not that special, they’re just different. A function expression is a so-called primary expression that can occur anywhere in code. It’s very clear from its syntactical form function …(…) {…} what elements it consists of, explicitly delimiting start and end.

In contrast, an arrow function can have an expression body without using a block, so it is not delimited explicitly. We therefore need restrictions on what things can appear inside the body of an arrow function and what things can not, to resolve ambiguity in a sequence of tokens containing a =>.

So my question is, what is the rule of precedence regarding arrow functions?

Basically, => has the same “precedence” as the = assignment operator.
(We just don’t like to call => an operator, as it constructs a value instead of computing a result after evaluating its operands)

This means that we can put an arrow function in any place where an assignment could occur:

  • as element values in array or object literals
  • as default initialisers (destructuring, function parameters)
  • as variable initialisers
  • as expression statements or in loop headers
  • as function arguments
  • as grouped expressions

Those are all unambiguous. The ambiguous cases are more interesting:

  • at the right hand side of an assignment
  • at the right hand side of the conditional operator
  • at the right hand side of the yield operator
  • at any side of the comma operator

Having to put an arrow function in a place where an assignment would be valid also means that we cannot put it (directly, without grouping) as an operand of any other (higher-precedence) operator – most notably not as an operand of logical expressions (||, &&).

The body of the arrow function can be an assignment expression (including anything of higher precedence) again, so the => is right-associative (just like =). The body will be parsed greedily (the expression is made as long as possible) so that it will contain basically all expressions on the right hand side of the =>.

The only things that cannot form an arrow function’s concise body are a yield expression (not allowed outside generator functions anyway) and a comma expression.

Read More:   Drag and drop sortable ng:repeats in AngularJS?

The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Similar Posts