15

This is related to Javascript a=b=c statements.

I do understand that

foo = foo.x = {n: b}; // console.log(foo) => {n: b}

but

foo.x = foo = {n: b}; // console.log(foo) => {n: b}

It should equal to :

foo = {n: b};
foo.x = foo; // console.log(foo) => {n: b, x:object}

Am I missing something here?

Community
  • 1
  • 1
Seeliang
  • 2,321
  • 1
  • 13
  • 18
  • console.log(foo.x) should print out the same thing. – HopefullyHelpful Jun 24 '16 at 08:18
  • I got an error "Cannot set property 'x' of undefined" – Flying Fisher Jun 24 '16 at 08:20
  • I don't see how the first line could work unless `foo` was *already* referencing some object. (And really the same applies to the second one, though intuitively the second "feels" like it should work as is.) – nnnnnn Jun 24 '16 at 08:42
  • 2
    You say what it *should* equal, without any justification. There is no justification, because the statement is false. **Can you explain to me why you believed this false thing**? It helps me to understand why people believe false things about programming languages. – Eric Lippert Jun 24 '16 at 17:59

4 Answers4

18

With:

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

The leading foo.x is partially evaluated first, enough to determine the exact target for the assignment, before proceeding to actually assign it.

It behaves more along the lines of:

var oldFoo = foo;
foo = {n: b};
oldFoo.x = foo;

This is mentioned in the standard. The left side of the = is evaluated (1.a) before the value is placed there (1.f):

AssignmentExpression : LeftHandSideExpression = AssignmentExpression

1) If LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral, then
a) Let lref be the result of evaluating LeftHandSideExpression.
...
f) Perform ? PutValue(lref, rval).

Jonathan Lonowski
  • 121,453
  • 34
  • 200
  • 199
11

This is because when you write

var foo = {};
foo.x = foo = {n: b} //a=b=c

while the line is being executed, foo is pointing to {} but when this statement is broken down to

foo.x = (foo = {n: b}) /a=(b=c)

foo's reference has changed from {} to {n:b} but foo in foo.x (a) is still pointing to old reference of foo since left hand expression was evaluated before assignment had begun.

As per the spec

  1. If LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral,

    a. then Let lref be the result of evaluating LeftHandSideExpression.

Which means before the assignment foo.x was still having reference to old foo.

So, if you tweak your example a little bit by doing

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

In this example, you didn't change the reference to foo, only assigned new property, so the output now is

Object {z: 2, n: Object, x: Object}

Now, it has retained the reference to old foo since a new reference was not assigned hence all properties z, n and x are being retained.

Chris
  • 27,210
  • 6
  • 71
  • 92
gurvinder372
  • 66,980
  • 10
  • 72
  • 94
6

It equals

let tmp = foo;
foo = {n: b};
tmp.x = foo;

You could see, that old foo (stored in z in this example) was modified:

> z=foo={};
{}
> foo.x = foo = {n: b};
{ n: 10 }
> foo
{ n: 10 }
> z
{ x: { n: 10 } }
Alexey Ten
  • 13,794
  • 6
  • 44
  • 54
  • The other answers describe that this is what's happening, but an example with `tmp` and `foo` initially having the same object as their value makes this much clearer. – Joshua Taylor Jun 24 '16 at 12:18
1

I got it.

var foo = {}; // now foo is a reference point to object {}
foo.x = foo = {n:1}; // first foo is refer to a new object {n:1}, then old foo referred object {} set a prop x

// try this to get what you want
var foo = foo1 = {};
foo.x = foo = {n:1};
console.log(foo, foo1) // here foo1 is what you want
Flying Fisher
  • 1,932
  • 2
  • 13
  • 13