@raina77ow recently helped me figure out computed property names. As part of their answer to my question, they shared a really tricky bit of code showcasing interesting aspects of JavaScript:
const increment = (() => { let x = 0; return () => ++x })();
const movingTarget = { toString: increment };
const weirdObjectLiteral = { [movingTarget]: 42 };
console.log( weirdObjectLiteral[movingTarget] ); // undefined
When I run that sample in the node CLI, that last line continually outputs undefined
, while the value x
in increment
continually increments.
If we replace const movingTarget = { toString: increment };
with const movingTarget = { [toString]: increment };
, this behaviour ceases to take place, and instead we get an output of 42
and the x
in increment
remains the same.
Can somebody help me understand why this is the case? What is it about JavaScript that makes things work this way?
Related Question: Does the x
in the function within increment
exist until we explicitly remove increment
from memory?
Lets evaluate the following object literal:
{[toString]: increment }
toString
is an identifier pointing to window.toString
(a function) As outlined by the answer, toString
will be called on that as object keys are always strings:
{[toString.toString()]: increment }
Now that results in something like:
{["function() { [native code] }"]: increment }
Now if we call toString()
on this object, the standard Object.prototype.toString
will get called in the {[movingTarget]: 42}
part and the result is [Object object]
(as always):
let x = 0;
let movingTarget = { ["function() { [native code] }"]: () => ++x };
console.log(
movingTarget.toString(), // [Object object]
{[movingTarget]: 42} // {["[Object object]"]: 42}
);
thats why the moving target isnt moving anymore. In the original code, toString
of the object was set, and that gets called whenever movingTarget
gets turned into a string:
let x = 0;
let movingTarget = { toString: () => ++x };
console.log(
movingTarget.toString(), // 1
"" + movingTarget, // 2
{[movingTarget]: 42} // { 3: 42 }
);