Chained Assignment in JavaScript

Chained Assignment in JavaScript

3月 25, 2021 · 3 分钟阅读时长 · 451 字 · -阅读 -评论

I ran into a seemingly easy JavaScript puzzle in a chat group today and still got it wrong, so I’m jotting it down.

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

Wrong Turn #1

I assumed it was equivalent to the code below:

var user = {
  n: 1
};
user = {
  n: 2
};
user.x = user;
console.log(user.x);
/**
 * {n: 2, x: {…}}
 */

Running it in the browser or Node showed the actual answer is undefined.

Wrong Turn #2

I went back to review the assignment operator. For chained assignment specifically:

Chaining the assignment operator is possible in order to assign a single value to multiple variables.

So a = b = c should behave like b = c; a = c;.

By that logic, I rewrote the snippet as:

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

I figured that must be correct, but the browser printed {n: 2}—still wrong. The missing piece? The member-access operator.

The Real Answer

The dot operator for member access has higher precedence than assignment.

Let’s analyze it again:

var user = {
  n: 1
};

user.x = {
  n: 2
};

user = {
  n: 2
};
console.log(user.x);

/**
 * user.x points to the original object (call it “1”).
 * The object literal {n: 2} is assigned to that original object's x.
 * The final assignment rebinds user to a new object (call it “2”).
 * console.log reads x from “2”, which has no such property, so the result is undefined.
 */

A simple question, yet it tripped me up—reminder that fundamentals matter. A few key takeaways:

  1. The dot operator has higher precedence than assignment.

  2. Chained assignment copies a single value into multiple variables, and it evaluates from right to left. You can prove this with a quick Proxy experiment:

    var superman = {
      name: 'clark'
    };
    
    var batman = {};
    var flashman = {};
    
    const batmanProxy = new Proxy(batman, {
      set: function (target, key, value) {
        console.log(`batman: ${key} set from ${target[key]} to ${value}`);
        target[key] = value;
        return true;
      }
    });
    
    const flashmanProxy = new Proxy(flashman, {
      set: function (target, key, value) {
        console.log(`flashman: ${key} set from ${target[key]} to ${value}`);
        target[key] = value;
        return true;
      },
      get: function (target, key) {
        console.log(`flashman: ${key} get from ${target[key]}`);
        return target[key];
      }
    });
    
    batmanProxy.name = flashmanProxy.name = superman.name;
    

    The logs show flashman’s setter firing first. Also note this is not the same as batmanProxy.name = (flashmanProxy.name = superman.name); because the proxy’s getter never runs.

  3. Object assignment copies references, not values.

Final Thoughts

Elementary question, valuable lesson. Back to studying—and back to slinging code.

Alan H
Authors
开发者,数码产品爱好者,喜欢折腾,喜欢分享,喜欢开源