7

这个 Javascript 表达式在所有浏览器(jsfiddle)中都可以正常工作:

false ? 1 : x = 2;

它评估为2。

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

(false ? 1 : x) = 2;

这个是扔一个ReferenceError。我仔细检查了Javascript 运算符优先级表,它指出条件运算符? :的优先级高于赋值运算符=,所以两个表达式应该是相同的,至少我是这样。

在 Java 中,它的语法和运算符优先级规则与 Javascript 非常相似,上面的两个表达式都会导致编译时错误,这是完全合理的。

有人可以解释这种区别吗?

4

3 回答 3

14

正如您在 MDN 中发现的,? :它的优先级高于赋值运算符=,这意味着 JS 将您的语句读取为:

false ? 1 : (x = 2);

乍一看,这似乎是倒退的,但它的意思是? :期待三个操作数,右边的部分:是第三个操作数。由于=具有较低优先级x = 2成为第三个操作数。

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

你的第二个版本:

(false ? 1 : x) = 2;

...给出一个参考错误,因为它(false ? 1 : x)首先执行评估与( )关联的值 的部分,它不返回变量本身。不起作用。xundefinedxundefined = 2

于 2013-06-01T11:46:37.327 回答
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 一直到基本上所有其他表达式,但赋值表达式最终让我们回到了开头 - 主表达式:

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

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

1

所有这些繁琐的结果(如果我是正确的)是在 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;}());

但是,在您的第二个片段中,当使用括号时,您明确地将条件表达式与“= 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 - 这很容易。

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

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

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

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

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

小提琴1

小提琴2

小提琴3

于 2013-10-09T02:17:29.720 回答
5

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

在表达式中

false ? 1 : x = 2

编译器将false视为条件,将1视为 if-true 和x=2。自从 ?优先于 = x=2尚未评估。因此,由于 false 本质上是 false,因此返回的是x=2

当计算x=2时返回 2。

因此为什么你可以写

x = y = 2

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

整个x=2被视为if-false操作数而不仅仅是x的原因是因为三元运算符将 : 右侧的所有内容视为if-false如果它在范围内。

但是,当您使用方括号时,您尝试将文字(无论 x 是什么)设置为 2,从而导致错误。

我无法回答为什么它在 javascript 中有效而在 java 中导致错误的问题。我只能假设运营商的优先级略有不同?

于 2013-10-06T18:41:19.833 回答