What is the difference between a shallow copy and a deep copy?
A shallow copy duplicates the top level of the object only. Every value inside is copied by reference, so nested objects are still shared between the original and the copy. A deep copy duplicates everything all the way down, so the two structures are completely independent.
const original = {
name: "Ada",
tags: ["a", "b"],
nested: { count: 1 },
};
// A shallow copy.
const shallow = { ...original };
shallow.nested.count = 99;
console.log(original.nested.count); // 99 - the inner object is shared
console.log(original.tags.length); // 2
// A deep copy.
const deep = structuredClone(original);
deep.nested.count = 0;
deep.tags.push("c");
console.log(original.nested.count); // 99 - untouched
console.log(original.tags.length); // 2 - untouchedThe shallow copy is the dangerous one because it looks like it worked. Adding or removing a top-level property on the copy leaves the original alone, so a quick test passes. The moment you reach one level deeper and mutate a nested object or array, both versions change together, and the bug appears far away from the assignment that caused it.
There are three ways to make a shallow copy. The spread operator in an object literal, the Object.assign method, and the structuredClone function with a shallow depth. They behave the same for plain data, and the spread syntax is the one to prefer because it also copies symbol-keyed properties in the same way the others do and reads more clearly.
Deep copies are more expensive and more limited. structuredClone handles plain objects, arrays, dates, maps, sets, typed arrays and circular references, but it cannot clone functions, it cannot clone class instances into plain objects with their methods intact, and it throws on a value carrying a function. For those cases you need a library such as the lodash cloneDeep function, or a hand-written recursive copy.