为什么三元运算符中的这个逗号会在 JavaScript 中引发语法错误?

Why does this comma inside a ternary operator throw a syntax error in JavaScript?

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

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, the conditional operator accepts two expressions as the 'if' and 'else' cases of the ternary operator, and the comma operator理论上是一个表达式

The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand.

那么为什么我会收到语法错误?逗号运算符是一个表达式,应该允许它出现在条件运算符中。虽然,将括号放在逗号的操作数两边效果很好:

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

为什么这样可以正常工作?括号(或 grouping operator)允许解释器 知道 它正在处理一个表达式,但是 console.log(a), a 已经 一个不需要括号的表达式,为什么没有括号我会出现语法错误?

这是语言的有意部分,在 ECMAScript Language Specification. The syntax for the comma operator is defined in Section 12.16 中进行了概述,其中说明如下:

12.16 Comma Operator ( , )

Syntax

Expression:
  AssignmentExpression
  Expression, AssignmentExpression

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

至于实际的条件运算符,运算符和条件表达式的语法在Section 12.14:

12.14 Conditional Operator ( ? : )

Syntax

ConditionalExpression:  
  LogicalORExpression  
  LogicalORExpression ? AssignmentExpression : AssignmentExpression

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

NOTE    The grammar for a ConditionalExpression in ECMAScript is slightly different from that in C and Java, which each allow the second subexpression to be an Expression1 but restrict the third expression to be a ConditionalExpression. The motivation for this difference in ECMAScript is to allow an assignment expression to be governed by either arm of a conditional and to eliminate the confusing and fairly useless case of a comma expression as the centre expression.

由于 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 允许它在三元运算符中,有关详细信息,请参阅


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

这个答案是 的延伸。专门用于显示语法中条件运算符允许 PrimaryExpressions(不包括逗号运算符)但不允许 Expressions(包括逗号运算符)的位置。


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

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

ConditionalExpression:
  LogicalORExpression
  LogicalORExpression ? AssignmentExpression : AssignmentExpression

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

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

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

Expression:
  AssignmentExpression
  Expression , AssignmentExpression

用更简单的话概括一下:JavaScript 的语法只允许 AssignmentExpression 中包含逗号运算符,前提是它包含在分组运算符 ().[=43= 中]

另见 Operator Precedence in JavaScript

资源