-2

I discovered a bug on a project I'm working on that can be replicated by this snippet:

const original = [ { value: 1 } ];
function test() {
    const copy = Object.assign([], original);
    copy.forEach(obj => obj.value = obj.value + 1);
}

console.log(original[0].value); // -> 1, expected 1
test();
console.log(original[0].value); // -> 2, expected 1
test();
console.log(original[0].value); // -> 3, expected 1

I do not understand why this is the case. In the MDN web docs, the following statements can be found in the deep copy warning section:

For deep cloning, we need to use alternatives, because Object.assign() copies property values.

If the source value is a reference to an object, it only copies the reference value.

How do these notes apply to arrays / in this case? Are array values somehow considered as properties?

Looking back now, the method was probably not intended to work with arrays, so I guess I reap what I sow... but I'd still like to understand what's going on here. The intent was to deep copy the array in order to mutate the objects inside while keeping the original intact.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
Khryus
  • 347
  • 3
  • 12
  • 1
    Arrays are objects so all the same concerns apply. – Etheryte Dec 13 '22 at 11:34
  • 1
    Exactly as you've quoted: _"For deep cloning, we need to use alternatives"_ to `Object.assign`, and you're showing why - that's a _shallow_ copy of the array. For another level of depth maybe you want e.g. `original.map((obj) => Object.assign({}, obj))` – jonrsharpe Dec 13 '22 at 11:35
  • Why do the statements talk about "property values"? Are array values somehow considered as properties of the array object? – Khryus Dec 13 '22 at 11:37
  • https://stackoverflow.com/a/5048482/3001761 – jonrsharpe Dec 13 '22 at 11:38
  • @Khryus — Of course they are. – Quentin Dec 13 '22 at 11:38
  • @SimoneRossaini unfortunately the project runs on an older version of Node, so the function is not supported. Thank you though, TIL. – Khryus Dec 13 '22 at 11:44

2 Answers2

1

Are array values somehow considered as properties?

Yes. In JavaScript, arrays are objects (which is why Object.assign works with them), and properties with a special class of names called array indexes (strings defining decimal numbers in standard form with numeric values < 232 - 1) represent the elements of the array. (Naturally, JavaScript engines optimize them into true arrays when they can, but they're defined as objects and performing object operations on them is fully supported.) I found this sufficiently surprising when getting deep into JavaScript that I wrote it up on my anemic old blog.

Given:

const obj = {a: 1};
const arr = [1];

these two operations are the same from a specification viewpoint:

console.log(obj["a"]);
console.log(arr["0"]); // Yes, in quotes

Of course, we don't normally write the quotes when accessing array elements by index, normally we'll just do arr[0], but in theory, the number is converted to a string and then the property is looked up by name — although, again, modern JavaScript engines optimize.

const obj = {a: 1};
const arr = [1];
console.log(obj["a"]);
console.log(arr["0"]); // Yes, in quotes
console.log(arr[0]);

If you need to clone an array and the objects in it, map + property spread is a useful way to do that, but note the objects are only cloned shallowly (which is often sufficient, but not always):

const result = original.map((value) => ({...value}));

For a full deep copy, see this question's answers.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Final solution was to switch to a deep clone. I am using lodash cloneDeep instead of structuredClone because of reliability and legacy reasons, and because structuredClone cannot copy function properties. – Khryus Dec 13 '22 at 12:10
-1

Here we can use structuredClone for deep copy.

KKR
  • 1
  • 1
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 16 '22 at 07:40