I want to do something to a couple of variables using operators in quick succession. I don't think what I want to do is important as such; my question is more about the fundamentals of JavaScript evaluation.
In the three examples below, I try to use addition to change the values of two variables. However, not all perform as I (perhaps naïvely) expected.
-
OPERATIONS AS THREE SEPARATE STATEMENTS
var a = 9, b = 2; a += b; b += a; a += b; // a === 24, b === 13
-
OPERATIONS SEPARATED BY COMMA OPERATOR
var a = 9, b = 2; a += b, b += a, a += b; // AS EXPECTED: a === 24, b === 13
-
OPERATIONS IN ONE STATEMENT/EXPRESSION
var a = 9, b = 2; a += (b += (a += b)); // BUT HERE WE GET THIS: a === 22, b === 13
In the last example, b
evaluates as expected, but a
evaluates to a number two short of what appears in the first two examples.
I think that this is because everything in the parentheses returns the correct value but is finally added to the original value of a
, i.e. 9
, rather than the value suggested by (a += b)
earlier in precedence which would be 11
.
I have looked for why this might be in Flanagan's JavaScript: The Definitive Guide (6th ed.), (particularly under 4.11.1 "Assignment with operation") but found nothing there. Crockford doesn't seem to mention it explicitly in The Good Parts either. I've used various search terms to try to find more information on this behaviour. Can anyone tell me what this phenomenon is called or point me to some information on this behaviour (assuming it is expected) or what I might be doing wrong (assuming it's not)?
NB. I'm conscious that the parentheses in example 3 may be redundant since, as I understand it, assignment precedence goes from right-to-left anyway. But I thought having them there would make the example easier to talk about.
UPDATE
Judging by the answers below, I think my confusion over this issue actually stems from having absorbed a couple of paragraphs from the Flanagan book, possibly incorrectly:
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
I took this to mean that my one line example should produce the same results as the other two, because:
- Flanagan mentions two evaluations occurring in
a = a op b
as opposed to one, implying this is in fact different toa op= b
wherea
is not evaluated as anlval
on the right. - I assumed the assignment operator I used (e.g.
a += b
) would count as a side-effect.
IMHO, I think Flanagan has made this confusing and it seems to contradict what's in the ECMAScript convention (as pasted below by pocka), but it could be my reading/misinterpretation. Is what he is saying incorrect or simply unclear? Or, is it just me?
Best Answer
I think (not sure, although this is counter-intuitive) you can imagine:
being written as:
Although the plus
+
operator has right to left associativity, JavaScript expressions are evaluated from left-to-right, soa
is evaluated first which is9
now, then(b += (a += b))
is evaluated to13
.Now the
+
operator adds from right-to-left, thus adding13
to9
and giving us22
.EDIT: I am not gonna comment directly on your questions because I feel confused by reading them :).
Instead I am gonna try to explain this differently. I think the main source of your confusion comes from the difference between operator precedence, associativity and order of evaluation.
I really advise you to read the part on the Order of Evaluation (4.7.7 in the book, which is a great book by the way).
Let's go with an example first:
In this example, although the multiplication operator
*
has higher precedence than the summation operator+
, the the evaluation of the different components of the entire expression is still from left-to-right.alpha
on the left is declared and created first, then(z=4)
is evaluated, theny
is evaluated to2
. Nowz
is evaluated again which results in4
, notice that this is the new value which is caused by the side-effect of assigning4
toz
earlier in the expression, remember(z=4)
.This results in an overall value for
alpha
that is equal to12
.Now back to our original expression:
a
on the left is evaluated first which is9
now, then the firstb
to the left is evaluated which is now2
, now the seconda
is evaluated which is9
also, then the lastb
to the right is evaluated which is again2
.Now starts the real work, because of the parentheses the last
(a += b)
is evaluated so now we havea = 11
, then(b += (a += b))
is evaluated which is now13
, now this value is summed the value already evaluated which is 9 resulting in22
.If it hasn't happened this way, this would mean that
a
on the left side of=
would have been evaluated twice which is not the case.Summary: You can't update the value of an already evaluated expression.
I hope this can clear this for you, if you have any further questions then feel free to ask :)