带条件和赋值运算符的表达式

时间:2013-06-01 11:42:56

标签: java javascript syntax operator-precedence

这个Javascript表达式适用于所有浏览器(jsfiddle):

false ? 1 : x = 2;

正在评估2。

但为什么呢?我希望这里有一个例外,因为赋值的左侧是false ? 1 : x,这不是有效的引用。与(jsfiddle)比较:

(false ? 1 : x) = 2;

这个投掷ReferenceError。我仔细检查了Javascript operator precedence table,它声明条件运算符? :的优先级高于赋值运算符=,因此两个表达式应该相同,至少我是这样。

在Java中,它具有非常相似的语法和运算符优先级规则,如Javascript,上述两个表达式都会导致编译时错误,这非常有意义。

有人可以解释这种差异吗?

3 个答案:

答案 0 :(得分:14)

正如您在MDN上发现的那样,? :具有比赋值运算符=更高的优先级,这意味着JS正在将您的语句读作:

false ? 1 : (x = 2);

初看起来可能看起来倒退了,但这意味着? :期待三个操作数,:右边的部分是第三个操作数。由于=优先级较低x = 2成为第三个操作数。

警报显示2,因为作业x = 2x变量设置为2,然后此(子)表达式的计算结果为2

你的第二个版本:

(false ? 1 : x) = 2;

...给出了引用错误,因为它首先执行(false ? 1 : x)部分,该部分计算与 xundefined)关联的值,它不会不返回变量x本身。 undefined = 2不起作用。

答案 1 :(得分:9)

以下是理解JavaScript条件表达式和Java条件表达式之间差异的两个关键:

请阅读ECMAScript 5注释规范本节底部的注意

http://es5.github.io/#x11.12

现在,请阅读条件表达式的Java规范部分:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25

您将注意到,正如ECMAScript 5注释所述,Java 中三元运算符中的第三个操作数不能只是任何旧表达式 - 它只能是ConditionalExpression。但是,对于ECMAScript 5,第三个操作数可以是任何AssignmentExpression。

进一步研究Java规范,我们看到Expression是任何赋值表达式:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.27

但是ConditionalExpression要么是带有三元运算符的ConditionalExpression(......?...:...),要么只是一个ConditionalOrExpression(在ES5中称为LogicalOrExpression)(请参阅上面前两个链接中的任意一个信息)。 ConditionalOrExpression可以从Java开始的“链”:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.24

在ECMAScript 5中:

http://es5.github.io/#x11.11

在ECMAScript 5规范中向后跟随表达式类型的“链”(因为它比Java规范更容易遵循)从ConditionalExpression开始,一直到基本上每个其他表达式,但是Assignment Expression最终将我们放在开头 - Primary表达:

http://es5.github.io/#x11.1

上面两个代码片段中的第二个操作数是一个主要表达式:

1

所有这些rigamarole(如果我是正确的)的结果是在Java中,三元运算符的第三个操作数不能是赋值,但在JavaScript中它可以。这就是为什么你的两个例子都在Java中失败但在JavaScript中只是第二个。

为什么第一个在JavaScript中工作但不在第二个工作?

operand1 ? operand2 : operand3;

以下IIFE代码(不是实际,但下面的代码说明了以上的工作方式) :

(function () { if (operand0) return operand1; else return operand2;}());

所以:

false ? 1 : x = 2;

成为(再次,实际上 - 以下代码说明上述代码):

(function () { if (false) return 1; else return x = 2;}());

然而,在你的第二个片段中,当使用parens时,你明确地将条件表达式与'= 2;'分开:

(false ? 1 : x) = 2;

成为(再次,实际上 - 以下代码说明上述代码):

(function () { if (false) return 1; else return x;}()) = 2;

三元运算符的“类似于示例IIFE函数调用的行为”行为将返回x是什么,并且将是,而不是无法分配的引用。因此错误。这将类似于以下代码(如果x === 3):

3 = 2;

显然,人们不能这样做。

在Java中,我相信,第一个给出错误,因为第三个操作符不能是赋值,第二个给出错误,因为你不能赋值(就像在JavaScript中一样)。

就运营商优先顺序而言,请查看以下代码:

var x = 3;

console.log(false ? 1 : x);          // ?: evaluates to "3"
console.log(false ? 1 : x = 2);      // ?: evaluates to "2"
console.log(false ? 1 : x = 2, 4);   // ?: evaluates to "2" - "2" and "4" arguments passed to log
console.log((false ? 1 : x = 2, 4)); // ?: evaluates to "4"

从上述IIFE说明性代码的角度来看,前两个很容易理解。

在第一行评估x并且条件表达式的计算结果为3 - 这很容易。

在第二行中,我能描述它的最好方法是条件运算符(?:)导致更低优先级'='运算符被评估为完整表达式,而不是因为(?:)具有更高的优先级,但是因为规范声明了':'后面的赋值表达式被评估(包括'= 2'部分)作为AssignmentExpression。在上面的IIFE示例的return语句中,此行为看起来更清晰。至少使用JavaScript,您不仅可以在第二个操作数中进行赋值,还可以在条件表达式的第三个中进行赋值。

但是,在第三行中,已在“x = 2”表达式中找到完整的赋值表达式,并且三元运算符将其用作完整的第三个操作数,并且“,”运算符的优先级低于其他任何运算符,我们得到相当于以下代码:

console.log((false ? 1 : x = 2), 4);

在第四行代码中,将整个表达式封装在parens的console.log()语句中,将',4'作为第三个操作数的一部分带入'?:'三元表达式。

以下jsfiddles使用实时代码演示了上述讨论。请注意,在打印“2”两次后,前两个具有相同的确切错误:

FIDDLE1

FIDDLE2

FIDDLE3

答案 2 :(得分:5)

三元运算符(?)需要三个值:A 条件 if-true if-false

在表达式

false ? 1 : x = 2

编译器将 false 视为条件,将 1 视为if-true和 x = 2 。既然?优先于= x = 2 尚未评估。所以,因为false本质上是假的,所以 x = 2 是返回的。

评估时 x = 2 返回2.

为什么你可以写

x = y = 2

在此示例中,x和y都设置为2。

整个 x = 2 被视为 if-false 操作数而不仅仅是 x 的原因是因为三元运算符需要如果 if-false 在范围内,那么右边的所有内容。

当你使用括号时,你要做的是将文字(无论x是什么)设置为2,导致错误。

我无法回答为什么在javascript中它可以工作的问题,但是它会导致java中的错误。我只能假设运营商优先级略有不同?