0

Here is a compound expression containing two assignment operators:

var a = {n: 1};
var b = a;
a.x = a = {m: 2};
a;    // => {m: 2}
b;    // => {n: 1, x: {m: 2}}

The tricky part is the third line:

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

IMHO, The assignment operator = is right-associative, so the nesting structure of the expression is:

a.x = (a = {m: 2});

But the evaluation order in ES5 is always from left to right, according to ES5 Annex D.

According to ES5 Section 11.13.1,

The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:

  1. Let lref be the result of evaluating LeftHandSideExpression.
  2. Let rref be the result of evaluating AssignmentExpression.
  3. Let rval be GetValue(rref).
  4. Throw a SyntaxError exception if the following conditions are all true: ....omitted intentionally to save space
  5. PutValue(lref, rval).
  6. Return rval.

So my understanding of the left-to-right evaluation order is :

  1. evaluate a.x first and return a reference lref1 for it
  2. evaluate a = {m: 2} to obtain rref1, because it is also an assignment expression, we'll start the procedure again (like recursion)

    2.1. evaluate a first and return a reference lref2 for it

    2.2. evaluate {m: 2} and return the very object {m: 2} as rref2

    2.3. Let rval2 = GetValue(rref2), so rval2 is also the object {m: 2}

    2.4. PutValue(lref2, rval2), so a will rebinds the object {m: 2} instead of {n: 1}

    2.5. return rval2, i.e. the object {m: 2} as rref1 (not Reference type, but object)

  3. Let rval1 = GetValue(rref1), which is also the very object {m: 2}

  4. PutValue(lref1, rval1), so the memory address which lref1 refers to will be {m: 2}. And b.x still refers to this address and b will be updated.

This procedure complies to the ES5 Spec and explains the result well.

My questions are:

  1. Is this evaluation order abovementioned true or false? If false, any alternative explanations?

  2. How to understand the Reference Specification Type in ES5 appropriately? Is it just an intermediate pointer which refers to certain memory address?

albert
  • 8,112
  • 3
  • 47
  • 63
leslie.zhang
  • 381
  • 4
  • 14
  • I'm not sure I get it, assigning a new value to `a` after `b = a` doesn't change `b`, changing `b` only happens if you change the properties of `a` ? – adeneo Jun 08 '15 at 18:26
  • http://jsfiddle.net/engvqu24/ – adeneo Jun 08 '15 at 18:28
  • Hi @adeneo, `b` is actually updated after the `a.x = a = {m:2}` expression, you can test it in console. – leslie.zhang Jun 08 '15 at 18:29
  • @adeneo pls note in the expression `a.x = a = {m: 2}`, both the rebinding and mutation of `a` occurred, so the evaluation order matters! – leslie.zhang Jun 08 '15 at 18:33
  • That's because `a.x = expression` is evaluated first, it's set to whatever the next expression returns, and an assignment always returns the assigned value, which is `{m: 2}`, so you get `b.x` before `a` is overwritten, is that what you're asking ? – adeneo Jun 08 '15 at 18:38
  • @adeneo Yes, I'm asking for the confirmation of evaluation order. Actually, we get `b.x` updated (Procedure 4) after `a` is overwritten (Procedure 2.4). – leslie.zhang Jun 08 '15 at 18:54
  • See also [Multiple assignment confusion](https://stackoverflow.com/questions/34025695/multiple-assignment-confusion) and [Javascript code trick: What's the value of `foo.x`](https://stackoverflow.com/q/32342809/1048572) – Bergi Apr 04 '19 at 13:24

1 Answers1

1

Yes, your understanding about the operator order appears to be correct.

ECMAScript 5 section 8.7 says:

A Reference consists of three components, the base value, the referenced name and the Boolean valued strict reference flag. The base value is either undefined, an Object, a Boolean, a String, a Number, or an environment record (10.2.1).

The process of creating a reference from property access is defined in 11.2.1:

  1. Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict.

Thus the reference lref1 holds the object value of a (along with the referenced name string "x"), which you originally created with {n: 1}. A reference does not care about the variable that it came from; it cares only about what base value and reference name it is supplied when created.

Altering what value is held by a has no effect whatsoever on the base value held by the reference lref1. lref1 continues to the hold the original value of a (i.e., that {n: 1} object) regardless of what a does after the creation of lref1.

In short, the reference created from the expression a.x ceases to have anything to do with the variable a as soon as the reference is created. Instead, the reference knows only about the value that was held by a at the time the reference was created.

apsillers
  • 112,806
  • 17
  • 235
  • 239
  • you mean `lref1` holds the reference of `a.x`, right? The base value of `lref1` should be `a`, the referenced name should be `"x"`. But pls not `a` doesn't has a `x` property, so it must create a `x` property here? – leslie.zhang Jun 08 '15 at 18:39
  • @LeslieZhang The base value of `lref1` should be the *value of `a`*, yes. Again, the reference has nothing more to do with the identifier or variable `a` after the reference is created. It only has a value (whatever was in `a` at the reference's creation time) and a reference name (`"x"`). /// The creation of the `x` property is handled by the [`PutValue`](http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.2) required by step 5 of the assignment steps. – apsillers Jun 08 '15 at 18:43
  • `It only has a value (whatever was in a at the reference's creation time) and a reference name ("x").` At the reference creation time, the value should be `undefined`. Because at creation time, `a === {n: 1}`, if we access `a.x`, we get `undefined`. – leslie.zhang Jun 08 '15 at 18:49
  • @LeslieZhang Note that I said, "whatever was in `a` at the reference's creation time" not "whatever was in `a.x` at the reference's creation time". A reference has two parts, called "base value" and "referenced name". The reference's base value here is the *initial value of `a`*, not `a.x`. The `x` component is stored separately, as a string, in the reference's "referenced name" component. The `PutValue` operation, in step 5 of assignment, is when we store a value in the `x` property of the base value held by the reference. – apsillers Jun 08 '15 at 18:52
  • @ apsillers That's what confused me, the reference type – leslie.zhang Jun 08 '15 at 19:00