在JavaScript中使用运算符链接变量

时间:2013-11-03 14:57:38

标签: javascript operators variable-assignment

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

在下面的三个示例中,我尝试使用addition来更改两个变量的值。然而,并非所有人都表现得像我(也许是天真的)预期的那样。

JSFiddle here.

  1. 作为三个单独声明的操作

    var a = 9, b = 2;
    a += b; b += a; a += b;
    // a === 24, b === 13
    
  2. COMMA OPERATOR分离的操作

    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
    
  4. 在最后一个示例中,b按预期进行评估,但a评估的结果是前两个示例中显示的数字。

    我认为这是因为括号中的所有内容都返回正确的值,但最终会添加到a的原始值,即9,而不是(a += b)建议的值先前优先级为11

    我已经找到了为什么这可能出现在Flanagan的 JavaScript:The Definitive Guide (第6版)中,(特别是在4.11.1“使用操作分配”中),但在那里什么也没找到。 Crockford似乎也没有在 The Good Parts 中明确提及它。我已经使用各种搜索词来尝试查找有关此行为的更多信息。任何人都可以告诉我这种现象被称为是什么或指向我关于这种行为的一些信息(假设它是预期的)或我可能做错了什么(假设它不是)?


    NB。我意识到示例3中的括号可能是多余的,因为据我所知,分配优先顺序从右到左。但我认为有它们会让这个例子更容易讨论。


    更新

    根据下面的答案判断,我认为我对这个问题的困惑实际上源于从Flanagan书中吸收了几段,可能是错误的:

      

    在大多数情况下,表达式为:

         

    a op= b

         

    其中 op 是一个运算符,相当于表达式:

         

    a = a op b

         

    在第一行中,表达式a被评估一次。在第二个,它被评估两次。仅当侧a包括诸如函数调用或增量运算符的副作用时,这两种情况才有所不同。例如,以下两个分配不同:

    data[i++] *= 2
    data[i++] = data[i++] * 2
    

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

    1. Flanagan提到在a = a op b而不是一个进行了两次评估,这意味着这与a op= b不同,其中a未被评估为右侧的lval
    2. 我认为我使用的赋值运算符(例如a += b)会被视为副作用。
    3. 恕我直言,我认为弗拉纳根已经让这个让人感到困惑,而且似乎与ECMAScript惯例中的内容相矛盾(正如下面pocka粘贴的那样),但这可能是我的阅读/误解。他说的不正确或根本不清楚?或者,这只是我吗?

2 个答案:

答案 0 :(得分:2)

我认为(不确定,虽然这是违反直觉的)你可以想象:

a += (b += (a += b));

写成:

a = a + (b += (a += b));

虽然加号+运算符具有从右到左的关联性,但JavaScript表达式是从左到右计算的,因此首先评估a,现在9,然后{{ 1}}被评估为(b += (a += b))

现在13运算符从右向左添加,从而将+添加到13并向我们9添加。

编辑:我不会直接评论您的问题,因为我在阅读时感到困惑:)。

相反,我会尝试以不同的方式解释这一点。我认为您混淆的主要原因来自运营商优先级,关联性和评估顺序之间的差异。

我真的建议你阅读评估顺序的部分(本书中的4.7.7,顺便说一下,这是一本很棒的书)

我们先来看一个例子:

22

在此示例中,尽管乘法运算符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 的优先级高于求和运算符*,但整个表达式的不同组件的评估仍然是从左到右。< / p> 首先声明并创建左侧的

+,然后评估alpha,然后将(z=4)评估为y。现在2再次进行评估,结果为z,请注意这是由于在表达式中先前将4分配给4的副作用而导致的新值,记住z

这导致(z=4)的整体价值等于alpha

现在回到我们原来的表达式:

12
左边的

a += (b += (a += b)); 首先评估a,然后评估左边的第一个9,现在是b,现在是第二个{{评估1}}也是2,然后评估右边的最后a,再次9

现在开始实际工作,因为括号中评估了最后一个b,所以我们现在2,然后(a += b)被评估,现在是a = 11,< strong>现在此值总和评估的值 9 ,结果为(b += (a += b))

如果没有这种方式发生,这意味着13左侧的22将被评估两次,而不是这种情况。

摘要:您无法更新已评估的表达式的值。

我希望这可以为你清楚,如果你有任何进一步的问题,那么随时可以问:)

答案 1 :(得分:2)

根据ECMA-262第5版。 11.13.2,复合赋值运算符的评估如下:

  
      
  1. 让lref成为评估LeftHandSideExpression的结果。
  2.   
  3. 让lval成为GetValue(lref)。
  4.   
  5. 让rref成为评估AssignmentExpression的结果。
  6.   
  7. 让rval为GetValue(rref)。
  8.   
  9. 设r是将operator @应用于lval和rval的结果。
  10.   
  11. 如果满足以下条件,则抛出一个SyntaxError异常:Type(lref)is Reference is true IsStrictReference(lref)is   true Type(GetBase(lref))是环境记录
      GetReferencedName(lref)是“eval”或“arguments”
  12.   
  13. 调用PutValue(lref,r)。
  14.   
  15. 返回r。
  16.   

最后一个例子评估如下:

1. Let a be lref, Let (b += (a += b)) be rlef.
2. Evaluate a and Let lval be the result of it(9).
3. Evaluate (b += (a += b)) and Let rval be the result of it.
   a. Let b be lref_, Let (a += b) be rlef_.   
   b. Evaluate b and let lval_ be the result of it(2).
   c. Evaluate (a += b) and let rval_ be the result of it.
     A. Let a be lref__, let b be rlef.
     B. Evaluate a and Let lval__ be the result of it(9).
     C. Evaluate b and Let rval__ be the result of it(2).
     D. Put lval__ + rval__ (means 9+2) to lref__(a) and return it.   
   d. Put lval_ + rval_ (means 2+11) to lref_(b) and return it.
4. Put lval + rval (means 9+13) to lref(a) and return it.

然后我们可以获得a === 22b === 13