【问题标题】:What is the exact parsing precedence of arrow function (fat arrow =>) in Javascript?Javascript中箭头函数(胖箭头=>)的确切解析优先级是什么?
【发布时间】:2018-10-31 21:01:30
【问题描述】:

我从eslint documentation 看到了一个关于箭头函数的例子:

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

所以我对箭头函数的优先级进行了一些研究。 => 似乎不被视为运算符,因为在 table of operator precedence on MDN 上找不到它。从页面arrow functions,它说

箭头函数具有特殊的解析规则,与常规函数相比,它们与运算符优先级的交互方式不同。

但没有进一步详细说明特殊的解析规则。所以我的问题是,关于箭头函数的优先规则是什么?


根据我的测试,它的优先级似乎高于赋值,但低于条件(三元)运算符?

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);

但我不确定这在不同的浏览器和平台上是否一致。任何人都可以对这种行为给出明确的答案吗?

【问题讨论】:

  • “但我不确定这在不同的浏览器和平台上是否一致。” 是的。 JavaScript 在the specification 中精确指定。很大程度上,不同 JavaScript 引擎以不同方式实现特定行为的时代在 IE11 中结束了。尚未完成的功能可能会有所不同,但到规范中时,它们是一致的。
  • fat arrow 语法不是“运算符”...只是表示箭头函数的语法。
  • 只有我一个不懂这个问题吗?
  • @BhojendraNepal 我在问 JS 解析器如何处理 => 与运算符的混合,如果这听起来更清楚的话。

标签: javascript ecmascript-6


【解决方案1】:

正如你所说,=> 不是运算符。箭头函数是主要语法。

它们的规则在规范中定义,以ArrowFunction production 开头。 ArrowFunction 被定义为 ArrowParameters 后跟 => 后跟误导性命名的 ConciseBodyConciseBody 有两种形式。您使用ExpressionBody 询问表单,这是=> 之后的第一个非空白标记不是 {。如果那里有一个左花括号,它将表示打开一个称为 FunctionBody 的块。

ExpressionBody 的定义非常简单:它是一个AssignmentExpression

这让我们进入了非常熟悉的领域,因为 AssignmentExpression 是赋值(或变量初始化器)右侧的语法、数组初始化器中的条目、属性的值部分初始化器、函数的参数等。因此,在简洁的正文中,=> 之后的任何内容都具有与我们在下面的 AssignmentExpression 中放置的相同的解析规则:

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

为了详细说明,AssignmentExpression 是:


(您可能想知道,as I didx = y 中的 y 如何匹配 AssignmentExpression 给定上面的定义,因为 y 显然是 Identifier 而没有其中的一些看起来会导致 Identifier 产生。这是规范可能很难阅读的地方。事实证明,如果你继续走足够长的时间,你就会到达那里。路径是(深呼吸): AssignmentExpressionConditionalExpression → LogicalORExpression → LogicalANDExpression → BitwiseORExpressionBitwiseXORExpressionBitwiseANDExpressionEqualityExpressionRelationalExpressionShiftExpressionAdditiveExpressionMultiplicativeExpressionExponentiationExpressionUnaryExpressionUpdateExpressionLeftHandSideExpressionNewExpressionMemberExpressionPrimaryExpression → IdentifierReferenceIdentifier——哇! [拖把眉毛]。谢谢奥里奥尔!)

【讨论】:

  • 现在,我明白了这个问题。 :)
  • @zypA13510 - 主要语法没有优先级或关联性。这就是您遵循解析器时语法的工作方式。但我想你可以这样想,是的。我发现上面的例子对理解简洁的主体特别有帮助的是y = AssignmentExpression, z = AssignmentExpression;,因为我非常熟悉它的解析方式,所以我可以很容易地将它扩展到箭头函数。例如,使用它作为我的参考点,我知道 result = array.reduce((a, v) => a * v, 1) 被解析 (cont'd)
  • (继续) 作为result = array.reduce(((a, v) => a * v), 1),而不是result = array.reduce((a, v) => (a * v, 1))。但我也知道 x = a => 1 ? 2 : 3 被解析为 x = a => (1 ? 2 : 3) 而不是 x = (a => 1) ? 2 : 3,只是因为长期熟悉。但就像一长串嵌套条件一样,每个人有时都会与箭头作斗争。 :-)
  • @zypA13510 - 恐怕我对规范使用的符号不够好,无法告诉你为什么callback = callback || () => {}; 不起作用。 (我试过了。使用的符号——对我来说——非常难以理解。)事实上,回答你的问题启发了我在 es-discuss 邮件列表上写我自己的question about the notation。 :-) 大概是第一个( 作为分组运算符,但我不能告诉你为什么。 :-( 它在某处。
  • 今天有个问题说清楚了,在语法中,all =>函数体是ConciseBody非终结符。 ConciseBodyAssignmentExpression 或(传统)FunctionBody
【解决方案2】:

特殊解析规则不再详述。

它们并没有那么特别,它们只是不同而已。 function 表达式是所谓的主表达式,可以出现在代码中的任何位置。从它的句法形式function …(…) {…} 可以很清楚地看出它由什么元素组成,明确地界定了开始和结束。

相比之下,箭头函数可以有一个表达式主体而不使用块,因此它没有明确分隔。因此,我们需要限制哪些东西可以出现在箭头函数的主体中,哪些东西不能出现,以解决包含=> 的标记序列中的歧义。

所以我的问题是,关于箭头函数的优先规则是什么?

基本上,=>= 赋值运算符具有相同的“优先级”。
(我们只是不喜欢将=> 称为运算符,因为它会构造一个值,而不是在评估其操作数后计算结果)

这意味着我们可以在任何可能发生赋值的地方放置一个箭头函数:

  • 作为数组或对象字面量中的元素值
  • 作为默认初始化器(解构、函数参数)
  • 作为变量初始化器
  • 作为表达式语句或在循环头中
  • 作为函数参数
  • 作为分组表达式

这些都是明确的。模棱两可的情况更有趣:

  • 在作业的右侧
  • 在条件运算符的右侧
  • yield 运算符的右侧
  • 在逗号运算符的任何一侧

必须将箭头函数放在赋值有效的位置也意味着我们不能将它(直接,不分组)作为任何其他(更高优先级)运算符的操作数- 最值得注意的是not as an operand of logical expressions (||, &&)

箭头函数的主体也可以是赋值表达式(包括任何更高优先级的),因此=> 是右关联的(就像=)。正文将被贪婪地解析(表达式尽可能长),以便它基本上包含=>右侧的所有表达式。

唯一不能形成箭头函数简洁主体的是yield 表达式(无论如何不允许在生成器函数之外)和逗号表达式。

【讨论】:

  • 这个答案需要更新,因为有关ConciseBodyFunctionBody 周围命名法的新信息曝光。
  • @Ben 我认为它没有,因为我没有提到那些非终结符,但你说得对,我已经澄清了
猜你喜欢
  • 1970-01-01
  • 2015-11-15
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 2021-06-23
  • 2016-03-24
  • 1970-01-01
相关资源
最近更新 更多