This is a matter of operator precedence, and the matter is that there are three operators involved here: =
, =
, and .
.
.
has higher operator precedence than =
, which means that a.x
is evaluated before the assignments take place. After that, reassigning a
mid-statement cannot affect the value of a
in the expression a.x
because it has already been evaluated.
We can observe this with the following code:
var _a;
var logging = false;
Object.defineProperty(window, 'a', {
get: function () {
if (logging) {
console.log('getting a');
}
return _a;
},
set: function (val) {
if (logging) {
console.log('setting a');
}
_a = val;
}
});
a = { n : 1};
var b = a;
logging = true;
a.x = a = {n: 2};
logging = false;
console.log(a.x);
console.log(b.x);
What we can see here is that a
's getter is being accessed before its setter in the process of the statement we're talking about, and it's only being accessed once. This tells us that a.x
is being evaluated before a.x = ....
.
Where you're going wrong is that that statement actually evaluates like this:
a = {n: 2}
[the value that a referred to before the statement started executing].x = {n : 2};
In other words, the identifier a
is "bound" before the statement begins executing and property references will refer to that bound value for the duration of the statement.
In this case, [the value that a referred to before the statement started executing]
is equivalent to b
, but it's not equivalent to the new value of a
. That's why b.x
has a value and a.x
does not.
You could say that the process works out to be something like this:
var a = { n : 1};
var b = a;
var temp = a;
var newVal = {n: 2};
a = newVal;
temp.x = newVal;
console.log(a.x);
console.log(b.x);
and this produces the same outcome as your original code.
The ECMAScript Spec lays out the procedure for evaluating an AssignmentExpression of the form LeftHandSideExpression = AssignmentExpression. The first two steps are:
- Let lref be the result of evaluating LeftHandSideExpression.
- Let rref be the result of evaluating AssignmentExpression.
This clearly shows that the left hand side of an assignment is evaluated before the right hand side is evaluated.