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.

A shallow copy duplicates the top level. Nested objects are still shared.

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
    const orig = { a: 1, nested: { b: 2 } };
    const copy = { ...orig };
2
    copy.a = 99;            // orig.a is 1        -- independent
    copy.nested.b = 99;     // orig.nested.b is 99 -- shared
3
The shallow tools.
    { ...obj }              spread
    Object.assign({}, obj)
    arr.slice()
    [...arr]
    Array.from(arr)
4
All of them copy one level. For an array of objects, the array is new and every element is the same object.
5
    const copy = [...users];
    copy[0].name = 'X';     // changes the original user too
6
structuredClone is the built-in deep copy.
    const deep = structuredClone(orig);
structuredClone
7
Handles nested structures, cycles, Map, Set, Date, RegExp and typed arrays. It throws on functions, DOM nodes and class instances — which lose their prototype in every approach anyway.
MapSetDateRegExp
8
The JSON round trip is what older code uses, and it is lossy:
9
    JSON.parse(JSON.stringify(orig))
10
    functions       -> removed
    undefined       -> removed (in objects) / null (in arrays)
    Date            -> string
    Map, Set        -> {}
    NaN, Infinity   -> null
    cycles          -> throws
11
For plain data it works. For anything else it silently changes the shape, which is the failure people do not notice until a Date is compared and does not match.
Date
12
Merging is shallow too.
    const merged = { ...defaults, ...options };
    // a nested object in options REPLACES the one in defaults,
    // rather than merging with it
13
A deep merge is a different operation and needs its own function or a library.
14
What to use.
    a flat object            spread
    nested plain data        structuredClone
    needs functions or
      class instances        a custom clone, or reconsider
    performance-critical     copy only what changes
15
And the alternative worth stating. Most of the time you do not need a deep copy at all — you need to not mutate:
16
    const next = { ...state, user: { ...state.user, name: 'X' } };
17
Copying only the path you are changing is what immutable updates do, and it is cheaper than cloning everything.
18
What the interviewer is probing.1. "What does spread copy?" Probing: the depth. Stalls: "Everything." Moves up: one level — nested objects are still shared references.
19
2. "What is wrong with JSON.parse(JSON.stringify(x))?" Probing: the losses. Stalls: "Nothing, it works." Moves up: it drops functions, undefined and symbols, turns Dates into strings, and throws on circular references.
20
3. "What is the modern answer?" Probing: the built-in. Stalls: "A library." Moves up: structuredClone, which handles Dates, Maps, Sets and cycles — though not functions.
21
4. "When is a shallow copy enough?" Probing: judgement. Stalls: "Never." Moves up: when the nested values are immutable or will be replaced rather than mutated, which is the normal case in state updates.