我想快速连续地使用运算符对几个变量做一些事情。我认为我想做的事情并不重要;我的问题更多是关于 JavaScript 评估的基础知识。
在下面的三个例子中,我尝试使用加法来改变两个变量的值。然而,并不是所有的表现都像我(也许天真地)预期的那样。
作为三个独立语句的操作
var a = 9, b = 2; a += b; b += a; a += b; // a === 24, b === 13
由逗号运算符分隔的操作
var a = 9, b = 2; a += b, b += a, a += b; // AS EXPECTED: a === 24, b === 13
一个语句/表达式中的操作
var a = 9, b = 2; a += (b += (a += b)); // BUT HERE WE GET THIS: a === 22, b === 13
在最后一个示例中,b
的计算结果符合预期,但 a
的计算结果比前两个示例中出现的数字小二。
我认为这是因为括号中的所有内容都返回了正确的值,但最终添加到 a
的原始值,即 9
,而不是建议的值通过 (a += b)
优先级较早,即 11
。
我已经在 Flanagan 的 JavaScript: The Definitive Guide(第 6 版)(特别是在 4.11.1“操作赋值”)中寻找这可能的原因,但在那里一无所获。 Crockford 似乎也没有在 The Good Parts 中明确提及它。我使用了各种搜索词来尝试查找有关此行为的更多信息。任何人都可以告诉我这种现象叫什么或指出有关此行为的一些信息(假设它是预期的)或我可能做错了什么(假设它不是)?
注意。我意识到示例 3 中的括号可能是多余的,因为据我所知,赋值优先级无论如何都是从右到左的。但我认为将它们放在那里会使示例更容易讨论。
更新
从下面的答案来看,我认为我对这个问题的困惑实际上源于吸收了弗拉纳根书中的几段,可能是错误的:
In most cases, the expression:
a op= b
where op is an operator, is equivalent to the expression:
a = a op b
In the first line, the expression
a
is evaluated once. In the second, it is evaluated twice. The two cases differ only if sidea
includes side effects such as a function call or an increment operator. The following two assignments, for example, are not the same:data[i++] *= 2 data[i++] = data[i++] * 2
我认为这意味着我的一行示例应该产生与其他两个相同的结果,因为:
- Flanagan 提到了
a = a op b
中发生的两个评估而不是一个,这意味着这实际上不同于a op= b
wherea
未被评估为右侧的lval
。 - 我假设我使用的赋值运算符(例如
a += b
)算作副作用。
恕我直言,我认为弗拉纳根让这件事变得令人困惑,而且它似乎与 ECMAScript 约定(如下由 pocka 粘贴)中的内容相矛盾,但这可能是我的阅读/误解。他说的是不正确的还是不清楚的?或者,这只是我吗?
最佳答案
我认为(不确定,虽然这是违反直觉的)你可以想象:
a += (b += (a += b));
写成:
a = a + (b += (a += b));
虽然加号 +
运算符具有从右到左的关联性,但 JavaScript 表达式是从左到右求值的,所以 a
首先求值,即 9
现在,(b += (a += b))
的计算结果为 13
。
现在 +
运算符从右到左添加,因此将 13
添加到 9
并得到我们 22
.
编辑:我不会直接评论你的问题,因为阅读它们让我感到困惑:)。
相反,我将尝试以不同的方式解释这一点。我认为您混淆的主要原因是运算符优先级、关联性和求值顺序之间的差异。
我真的建议你阅读有关评估顺序的部分(书中的 4.7.7,顺便说一句,这是一本很棒的书)。
我们先来看一个例子:
var x =1, y = 2, z = 3;
var alpha = (z=4) + y * z;
console.log(x); // 1
console.log(y); // 2
console.log(z); // 4
console.log(alpha); // 12
在这个例子中,虽然乘法运算符*
的优先级高于求和运算符+
,但是整个表达式的不同分量的求值仍然是从左开始-向右。
alpha
先被声明和创建,然后(z=4)
被评估,然后y
被评估为2
。现在 z
再次计算结果为 4
,请注意这是由将 4
分配给的副作用引起的新值z
前面的表达式,记住 (z=4)
。
这导致 alpha
的总值等于 12
。
现在回到我们原来的表达:
a += (b += (a += b));
左边的 a
首先被求值,现在是 9
,然后左边的第一个 b
被求值,现在是 2
,现在第二个 a
也被计算为 9
,然后右边的最后一个 b
被计算为再次 2
。
现在开始真正的工作,因为括号对最后一个 (a += b)
进行了计算,所以现在我们有 a = 11
,然后是 ( b += (a += b))
被求值,现在是 13
,现在这个值是 already 的总和评估结果为 9,结果为 22
。
如果它没有以这种方式发生,这将意味着 =
左侧的 a
将被计算两次,但事实并非如此。
总结:您无法更新已计算表达式的值。
我希望这可以为您解决这个问题,如果您有任何其他问题,请随时提问:)
关于javascript - 在 JavaScript 中使用运算符对变量进行链式赋值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19754271/