All topics
Objectsintermediate

Shallow Copy vs Deep Copy

The difference between copying only an object's top level versus recursively copying every nested structure, and the tools available for each.

Copying objects in JavaScript is trickier than it looks because of reference semantics: a naive copy often only duplicates the outermost layer, leaving nested objects and arrays shared between the original and the 'copy' — a shallow copy. A deep copy duplicates every level of nesting so the two structures are completely independent. Interviewers ask about this because it's a near-guaranteed source of real bugs, especially in state-management code that assumes immutability.

A shallow copy is like photocopying the cover of a binder but leaving the actual inserted folders inside untouched and shared with the original binder — flip open either binder's folder and you're looking at the exact same physical pages. A deep copy is like photocopying every single page inside every folder too, so the two binders share nothing physically afterward.

Key Concepts

1
Spread syntax ({ ...obj }, [...arr]) and Object.assign({}, obj) both create shallow copies: they copy each of the object's own top-level properties into a new object, but if any of those property values are themselves objects or arrays, only the *reference* to that nested structure is copied, not a duplicate of it — mutating the nested object through the copy also mutates it through the original, since both point at the same underlying data.
{ ...obj }[...arr]Object.assign({}, obj)
2
For genuine deep copies, structuredClone(obj), a relatively modern built-in global function, recursively clones an object including nested objects, arrays, Maps, Sets, and even circular references, without needing any external library — this has largely replaced the old JSON.parse(JSON.stringify(obj)) trick, which technically works for simple plain-data objects but silently breaks on functions (dropped entirely), undefined values (dropped), Date objects (converted to strings), and circular references (throws an error).
structuredClone(obj)JSON.parse(JSON.stringify(obj))undefinedDate
3
For more control — like cloning some parts deeply and others shallowly, or excluding certain properties — a manual recursive clone function or a library like Lodash's cloneDeep is often used. The practical rule of thumb: reach for a shallow copy (spread/Object.assign) when you know the object is flat or you specifically want to share nested references intentionally, and reach for structuredClone or a deep-clone utility whenever nested mutation independence actually matters.
cloneDeepstructuredClone