9

I'm a newby of JavaScript and just met this question. Can't figure it out by googling and searching on stackoverflow. Code snippet is as following:

var a = { n : 1};    
var b = a;    
a.x = a = {n:  2};    
console.log(a.x);
console.log(b.x);

According to my current knowledge, a.x = a = {n:2}; equals to :

a = {n : 2};
a.x = a;

Which eventually make a equals to {n:2, x:{n:2}}. So a.x should equal to {n:2}, and because b = a, so b.x = {n:2}. But the result I run in browser is : alert(a.x) is undefined and alert(b.x) is [object object].

Can someone explain why? thanks a lot.

Sterling Archer
  • 22,070
  • 18
  • 81
  • 118
Lisa
  • 150
  • 2
  • 9

4 Answers4

7

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:

  1. Let lref be the result of evaluating LeftHandSideExpression.
  2. 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.

JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • 2
    This does not have anything to do with precedence (or associativity). Rather, it's the order in which operands are evaluated, specifically the operands `a.x` and `a = {n: 2}`. The reason the result is surprising (to me) is that `=` is right-associative, but still evaluates operands left-to-right. Other than that nitpick, your answer is correct and very informative. – Kendall Frey Feb 27 '18 at 16:01
0

a.x in this scenario is undefined because

a reference in a.x has been changed to new reference value due to second assignment ( a = {n:2})

Reading value of property with old reference is undefined , as it got changed

To see difference add new property , something like this

a.x = a.y = {n:2} // this will not change the reference of a

console.log(a.x);//{n:2}
console.log(a.y);//{n:2}
console.log(b.x);//{n:2}

https://codepen.io/nagasai/pen/JpeENv

More details from the similar question here - How does a.x = a = {n: b} work in JavaScript?

Naga Sai A
  • 10,771
  • 1
  • 21
  • 40
0

You've run into something called operator precedence/associativity.

Consider their example:

a = b = 5;

with the expected result that a and b get the value 5. This is because the assignment operator returns the value that is assigned. First, b is set to 5. Then the a is also set to 5, the return value of b = 5, aka right operand of the assignment.

This breaks down to:

b = 5
a = the return of b which is now 5.

a.x = a = {n:  2};

Breaks down into:

a = {n: 2}
a.x = the return of a = {n: 2}

Since b is a reference to a, it is altered after the assignment.

Sterling Archer
  • 22,070
  • 18
  • 81
  • 118
0

The code

a.x = a = {n:2};

is the same as

var a = b = 1;

both variables a and b will be set to 1;

Now since you are working with an object and assigning a value to itself ( you would actually be creating a loop, but non the less the routine in JS gets confused ( which it should ).

basically the issue is the order of evaluation.

This is just my take on it after experimenting with it. I Would like to hear what other answers the community has though.

Plixxer
  • 466
  • 4
  • 15