Chained Assignment in JavaScript
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:
The dot operator has higher precedence than assignment.
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 asbatmanProxy.name = (flashmanProxy.name = superman.name);
because the proxy’s getter never runs.Object assignment copies references, not values.
Final Thoughts
Elementary question, valuable lesson. Back to studying—and back to slinging code.