25

This code:

var foo = {n: 1};
var bar = foo;
foo.x = foo = {n: 2};

Can you please explain what is meant by:

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

I see that {n:2} is assigned to foo. Why is undefined assigned to foo.x? Does foo = {n: 2}; return undefined?

Nayuki
  • 17,911
  • 6
  • 53
  • 80
Nicolas S.Xu
  • 13,794
  • 31
  • 84
  • 129

4 Answers4

25

According to the spec, the left hand side of an assignment expression is evaluated first, even though the operator has right-to-left precedence. Thus the expression foo.x = foo = {n: 2} which is the same as foo.x = (foo = {n: 2}) is evaluated like this:

  1. Evaluate the left-hand expression foo.x to get a reference, which is where the value of the right-hand expression will be assigned to.

  2. Evaluate the right-hand expression, to to get the value that will be assigned. The right-hand side is another assignment expression, so it gets evaluated the same way:

    1. Evaluate foo to determine where to assign to.
    2. Evaluate the expression {n:2}, which creates an object, to determine the value to assign.
    3. Assign {n:2} to foo, and return {n:2}.

  3. Assign the value that the expression on the right-side evaluated to ({n:2}), to the reference that foo.x resolved to in step 1 (before foo was assigned a new value). Which is also the same as bar.x, because of the assignment bar = foo on the line before.

When this is done, the original object, that bar is still a reference to, will have an x property that references the second object created. foo is also a reference to that second object, so foo === bar.x.

Paul
  • 139,544
  • 27
  • 275
  • 264
8

Because the property access foo.x on the left is evaluated before the right-hand side.

Let's make it more clear what your code actually does, by giving new names to the temporary expressions being evaluated:

var foo0 = {n: 1};
var foo1 = {n: 2};
foo0.x = foo1;
foo0 = foo1;
console.log(foo0.x);

Hence foo0.x is foo1.x is undefined.

In your original code, you can see that bar.x is {n: 2}, confirming this explanation.

Nayuki
  • 17,911
  • 6
  • 53
  • 80
4

According to the JavaScript spec, left hand side is always evaluated first:

12.14.4 Runtime Semantics: Evaluation

AssignmentExpression[In, Yield] : LeftHandSideExpression[?Yield] = AssignmentExpression[?In, ?Yield]

If LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral, then
1. Let lref be the result of evaluating LeftHandSideExpression.

http://www.ecma-international.org/ecma-262/6.0/index.html#sec-assignment-operators-runtime-semantics-evaluation

It can be clearly seen if you add another reference to the foo object:

var ref = {n:1};
var foo = ref;
var bar = foo;
foo.x = foo = {n: 2};

ref.x exists because foo.x refers to the unmodified value of foo.

Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247
0

Because you re-assigned foo to an object with a single property, { n: 2 }.

In JavaScript, expressions are evaluated from right to left. So when you try to access foo.x, you are trying to get the value of x from newly assigned value of foo, { n: 2 }, which is undefined.

Mario Pabon
  • 605
  • 4
  • 8