javascript - 在 JavaScript 中使用运算符对变量进行链式赋值

标签 javascript operators variable-assignment

我想快速连续地使用运算符对几个变量做一些事情。我认为我想做的事情并不重要;我的问题更多是关于 JavaScript 评估的基础知识。

在下面的三个例子中,我尝试使用加法来改变两个变量的值。然而,并不是所有的表现都像我(也许天真地)预期的那样。

JSFiddle here.

  1. 作为三个独立语句的操作

    var a = 9, b = 2;
    a += b; b += a; a += b;
    // a === 24, b === 13
    
  2. 由逗号运算符分隔的操作

    var a = 9, b = 2;
    a += b, b += a, a += b;
    // AS EXPECTED: a === 24, b === 13
    
  3. 一个语句/表达式中的操作

    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 side a 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

我认为这意味着我的一行示例应该产生与其他两个相同的结果,因为:

  1. Flanagan 提到了 a = a op b 中发生的两个评估而不是一个,这意味着这实际上不同于 a op= b where a 未被评估为右侧的 lval
  2. 我假设我使用的赋值运算符(例如 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/

相关文章:

r - 如何将 cat 的输出分配给一个对象?

javascript - Electron 应用程序显示白屏但在 CentOS 8 上呈现

c# - 如何覆盖一个操作符?

python - 算术运算符

php - 帮助此错误消息>意外的T_OBJECT_OPERATOR

C 给变量加上负号而不给它赋值

javascript - 如何在 JavaScript 中获取键/值对中的值 - Google Trends API

javascript - 禁用 :hover during Css animation

javascript - 使用 jquery resizable 调整 DIV 的句柄大小

python - 类变量在赋值时失去作用域