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)) console.log(x); console.log(a);
But I'm not sure if this is consistent on different browsers and platforms. Can anyone give a definitive answer to this behavior?
It's a new feature that introduced in ES6 and is called arrow function. The left part denotes the input of a function and the right part the output of that function.
Arrow function — also called fat arrow function— is a new feature introduced in ES6 that is a more concise syntax for writing function expressions. While both regular JavaScript functions and arrow functions work in a similar manner, there are certain differences between them.
What It Is. This is an arrow function. Arrow functions are a short syntax, introduced by ECMAscript 6, that can be used similarly to the way you would use function expressions. In other words, you can often use them in place of expressions like function (foo) {...} .
Arrow function is one of the features introduced in the ES6 version of JavaScript. It allows you to create functions in a cleaner way compared to regular functions. For example, This function // function expression let x = function(x, y) { return x * y; }
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); 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!)
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:
Those are all unambiguous. The ambiguous cases are more interesting:
yield
operatorHaving 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With