【问题标题】:Why does this comma inside a ternary operator throw a syntax error in JavaScript?为什么三元运算符中的逗号会在 JavaScript 中引发语法错误?
【发布时间】:2018-06-10 20:57:42
【问题描述】:

尝试在条件(三元)运算符中使用逗号运算符进行日志记录时,我注意到一些奇怪的事情。这是一个人为的例子:

const a = 2;
const b = 1;
a > b ? console.log(a), a : b; //I expect this to log and evaluate to a

但我却遇到了这个:

Uncaught SyntaxError: Unexpected token ,

根据MDN documentation,条件运算符接受两个表达式作为三元运算符的'if'和'else'情况,而comma operator理论上是一个表达式为,

逗号运算符计算其每个操作数(从左到右)并返回最后一个操作数的值。

那么为什么会出现语法错误?逗号运算符是一个表达式,应该允许在条件运算符中。虽然,在逗号的操作数周围加上括号可以正常工作:

a > b ? (console.log(a), a) : b; //Logs and gives a

为什么效果很好?括号(或grouping operator)允许解释器知道它正在处理一个表达式,但console.log(a), a 已经是一个不需要括号的表达式,那么为什么要这样做没有它们我会收到语法错误?

【问题讨论】:

  • 我做了一些编辑,但我没有删除ecmascript-6标签事件虽然我认为它甚至适用的唯一原因是你不必要地使用@987654329 @关键字而不仅仅是var
  • @GeorgeJempty 我只选择 ECMAScript(没有版本),因为我在答案中使用了规范,但它不存在,尽管这是一个好点。我会说你的编辑很好,谢谢。
  • 很高兴您可以在标题中添加“三元运算符”,我认为这会让它更适合 google。总而言之,自我回答的问答非常棒,我自己做过一些,但他们从来没有得到过这样的接待。

标签: javascript ecmascript-6 syntax-error language-lawyer ternary-operator


【解决方案1】:

这是语言的有意部分,并在ECMAScript Language Specification 中进行了概述。逗号运算符的语法在Section 12.16 中定义,其中声明如下:

12.16 逗号运算符 (, )

语法

表达式: 赋值表达式 表达式、赋值表达式

这里,规范概述了如何使用逗号运算符。 Expression 是任何 AssignmentExpression 或其本身,后跟逗号(运算符)和另一个 AssignmentExpression。需要注意的重要一点是 AssignmentExpressionExpressionExpression不是 一个AssignmentExpression

至于实际的条件运算符,运算符和条件表达式的语法在Section 12.14中有详细说明:

12.14 条件运算符 (? : )

语法

条件表达式逻辑或表达式 逻辑或表达式?赋值表达式:赋值表达式

根据规范,条件表达式只能包含 AssignmentExpression s - 而不仅仅是 Expression s。因此,条件运算符不能在其操作数中包含逗号运算符。这似乎是一种奇怪的语言怪癖,但考虑到非常具体的语法和规范,这是有特定原因的:

注意 ECMAScript 中 ConditionalExpression 的语法与 C 和 Java 中的语法略有不同,它们都允许第二个子表达式为 Expression1 但限制第三个表达式为ConditionalExpressionECMAScript 中这种差异的动机是允许赋值表达式由条件的任一分支控制以消除逗号表达式作为中心表达式的混乱和相当无用的情况。

由于 Java 和 C 的限制性语法,它们不允许这样的事情(Java):

int a = 2;
int b = 1;
System.out.println(a > b ? b = a : a = b); //Can't use assignment in 'else' part
//                                 ^^^^^

ECMAScript 作者决定允许在三元运算符的两个分支中进行赋值,因此出现了 AssignmentExpression 的定义。因此,这个定义也不允许逗号运算符实际出现在条件运算符的“if”部分,但由于它的稀缺性和无用性,这不是问题。他们基本上是用一块石头杀死了两只鸟。允许更宽松的语法,并摆脱了不好的无用语法。

添加分组运算符允许它工作的原因是因为分组运算符产生 ( Expression ) 根据定义也是一个 AssignmentExpression 允许它在三元运算符,详情请参阅str's answer


1这是指Java 的Expression,而不是ECMAScript 的Expression。 Java 确实没有有逗号运算符,所以它的 Expression 不包括它。

【讨论】:

    【解决方案2】:

    此答案是Li357's answer 的扩展。专门显示条件运算符在语法中的哪个位置允许PrimaryExpressions(不包括逗号运算符)但不允许Expressions(包括逗号运算符)。


    请参阅此答案底部提到的每种表达式或运算符类型的规范链接。

    条件运算符的规范定义如下:

    ConditionalExpression:
      LogicalORExpression
      LogicalORExpression ? AssignmentExpression : AssignmentExpression
    

    因此,它可以是一个LogicalORExpression,也可以是一个LogicalORExpression 和两个AssignmentExpressions 的组合。 AssignmentExpression 本身也可以由 LogicalORExpression 指定。

    但与其简单的名称不同,LogicalORExpression 不仅仅是一个基本条件,它本身还可以包含许多不同的嵌套表达式。 一直到PrimaryExpression,其中还包括分组表达式(Expression)

    并且从逗号运算符的规范中可以看出,它只在Expression中指定,而不是在PrimaryExpression本身中指定。

    Expression:
      AssignmentExpression
      Expression , AssignmentExpression
    

    简而言之:JavaScript 的语法只允许在 AssignmentExpression 中使用逗号运算符,前提是它包含在分组运算符 () 中。

    另见Operator Precedence in JavaScript

    资源

    【讨论】:

    • 我打算把它包含在我的里面,但我觉得它太多了,只会拉长答案而没有多大价值。但作为一个扩展,这很好用。 +1
    • "运算符优先级有点相关" - 不,它不是“有点相关”,整个问题都是关于运算符优先级的! :-D
    • @Bergi 规范中没有具体记录优先级(至少我没有找到任何东西),而是由语法隐式给出的。这以及 MDN 不是 JavaScript 的权威来源这一事实是我这样表述的原因。但你当然是对的,我改变了那句话。
    猜你喜欢
    • 2013-11-09
    • 2013-01-23
    • 1970-01-01
    • 2012-11-19
    • 2012-10-01
    • 2021-09-07
    • 2022-11-25
    • 1970-01-01
    相关资源
    最近更新 更多