A shallow copy duplicates only the top-level structure and keeps nested object references shared, while a deep copy recursively copies everything. Knowing the difference is critical to avoid accidental mutations in state management, React/Angular, and general JavaScript code.
Shallow Copy vs Deep Copy in JavaScript (Objects, Arrays, References)
Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.
The core idea
In JavaScript, objects and arrays are reference types. A “copy” can either:
- Shallow copy: copy only the top-level container, but keep nested objects shared.
- Deep copy: copy everything recursively, so no nested references are shared.
Most bugs around state mutation come from thinking you made a deep copy but actually making a shallow one.
Type | What gets copied | What stays shared | Mutation risk |
|---|---|---|---|
Shallow copy | Only the first level | All nested objects/arrays/functions | High (mutating nested data affects original) |
Deep copy | All levels recursively | Nothing (new references everywhere) | Low (copies are fully independent) |
Shallow copy examples
Common ways to make shallow copies:
const original = {
name: 'Alice',
address: { city: 'Paris' }
};
// Shallow copies:
const a = { ...original }; // spread
const b = Object.assign({}, original);
// Top-level is new:
a !== original; // true
// Nested object is shared:
a.address === original.address; // true
// Mutating nested object affects both:
a.address.city = 'London';
console.log(original.address.city); // 'London' ❗
Deep copy examples
const original = {
name: 'Alice',
address: { city: 'Paris' }
};
// 1) structuredClone (modern, correct for most cases)
const deep1 = structuredClone(original);
// 2) JSON trick (limited!)
const deep2 = JSON.parse(JSON.stringify(original));
// Check:
deep1.address === original.address; // false
deep1.address.city = 'Rome';
console.log(original.address.city); // 'Paris' ✅
Why JSON.stringify is dangerous
It silently breaks or loses:
undefined,Symbol, functionsDate,Map,Set,RegExp- Circular references (throws)
So it only works for plain data objects.
What about arrays?
Same rules apply. [...arr] and arr.slice() are shallow.
const arr = [{ x: 1 }, { x: 2 }];
const copy = [...arr]; // shallow
copy[0].x = 999;
console.log(arr[0].x); // 999 ❗
Where this matters in real life
- React / Angular / Redux / NgRx state updates
- Undo/redo stacks
- Caching snapshots
- Any place you assume “old state” is immutable
A shallow copy of a nested state tree is not enough.
Performance reality check
- Deep copying large objects is expensive.
- In practice, you often do structural sharing: deep copy only the path you change, keep the rest shared (like Redux/Immer).
This is why libraries like Immer exist.
One-sentence answer
A shallow copy creates a new top-level object but reuses nested references, while a deep copy recursively duplicates everything so changes in one copy never affect the other.