Shallow vs deep copy: why changing a copy changed the original
Spread and Object.assign copy only the top level. Learn reference semantics, what structuredClone can and cannot copy, and why a JSON round-trip loses data.
Comes up in: JavaScript, Coding
Learn#
Pooja builds an “edit order” screen. She copies the order with { ...order }, lets the user change a quantity, and clicks Cancel. The original order still shows the new quantity. Cancel did not cancel.
Variables hold objects by reference: they point to the object; they are not the object. Copying a variable copies the pointer.
Spread { ...obj }, [...arr] and Object.assign make a shallow copy: a new outer object whose properties still point to the same nested objects.
A deep copy copies nested objects too. structuredClone(value) does that using the structured clone algorithm. It handles Date, Map, Set, typed arrays and even cycles. It cannot copy functions, DOM nodes or class prototypes — class instances come back as plain objects.
The old trick, JSON.parse(JSON.stringify(x)), loses more: dates become strings, undefined and functions vanish, Map and Set become {}, and cycles throw.
const order = { id: 1, items: [{ sku: "tea", qty: 1 }], placed: new Date("2026-10-31T00:00:00Z") };
const shallow = { ...order };
shallow.items[0].qty = 5;
console.log(order.items[0].qty, shallow.items === order.items);
const deep = structuredClone(order);
deep.items[0].qty = 9;
console.log(order.items[0].qty, deep.placed instanceof Date);
console.log(typeof JSON.parse(JSON.stringify(order)).placed);
try { structuredClone({ save() {} }); } catch (e) { console.log(e.name); }Output (verified)
5 true 5 true string DataCloneError
order
- id: 1
- items → [array A]
copy
empty
array A
- { sku: 'tea', qty: 1 }
order is an object. Its items property points to an array stored elsewhere.
Read the steps as text
- order is an object. Its items property points to an array stored elsewhere.
- { ...order } copies the top level: a new object with the same values — including the same pointer to array A.
- copy.items[0].qty = 5 follows the shared pointer. Both objects now see qty 5.
Predict#
Predict first
const a = { tags: ['x'] }; const b = { ...a }; b.tags = ['y']; What is a.tags?
b.tags = ['y'] replaces b’s pointer with a new array; a still points to the old one. Shallow copies only bite when you change a shared nested object, like b.tags.push('y').
Interview angle#
Say it like this — about 30 seconds
“Objects are held by reference. Spread and
Object.assignare shallow: the new object’s nested objects are the same ones as before, so changing them changes the original.structuredClonemakes a deep copy and supports dates, maps, sets, typed arrays and cycles, but not functions, DOM nodes or prototypes. JSON round-trips lose dates,undefined, functions, maps and sets. In UI state I usually avoid deep copies and copy only the path I change.”
Follow-up What does structuredClone not support?
Follow-up How do you compare two objects for equality?
=== only checks identity. For content, write or use a deep-equal function that compares keys and values recursively (JS.15.07).Follow-up Why is copying only the changed path better in React or Redux?
Practice#
faded
moveUser changes the original user, because spread is shallow. Copy only the path you change, and share the rest.
Done when
- The original user and its address are unchanged.
- The result has the new city, and a new
addressobject. - Untouched nested data, like
tags, is the same object as before (shared, not copied).
Remember#
Try to answer each card before you open it. Then add them to your reviews.
What does a shallow copy share with the original?
All nested objects and arrays.
Name three things structuredClone can copy that JSON cannot.
Date, Map, Set (also cycles and typed arrays).
What can structuredClone not copy?
Functions, DOM nodes and prototypes (class instances become plain objects).
What does this print?const a = { n: { v: 1 } };
const b = { ...a };
b.n.v = 2;
console.log(a.n.v);
const a = { n: { v: 1 } };
const b = { ...a };
b.n.v = 2;
console.log(a.n.v);2
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Copies made with spread are new at the top level only. Change nested data through a copy and the original changes too. Copy the path you change, or deep-copy with structuredClone.
Check yourself: After const b = [...a], does b.push(1) change a?
No — b is a new array. Only changes to shared nested objects leak.
How it works underneathSenior rounds
The structured clone algorithm is defined in the HTML Standard; the same algorithm moves data through postMessage and IndexedDB Source: HTML Standard: Structured cloning. Unsupported values throw a DataCloneError Source: MDN: The structured clone algorithm.
Check yourself: Which standard defines structuredClone?
The HTML Standard (the structured clone algorithm).
Edge cases and trapsWhere people slip
JSON drops keys whose value is undefined or a function, turns NaN and Infinity into null, and turns Map and Set into {}. structuredClone keeps object identity inside the value: if two keys point to one object, the clone’s keys point to one new object.
Check yourself: What does JSON.stringify do with undefined values in an object?
It leaves those keys out.
In productionStaff-level answers
State libraries rely on copying only changed paths: Redux reducers, React setState with spread, or Immer, which lets you “mutate” a draft and creates the minimal copy. Freeze objects in development to catch accidental mutation (JS.06.04).
Check yourself: What does Immer do for you?
Lets you write mutating code on a draft and produces an immutable copy of only the changed paths.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-10-31. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- What is the difference between a shallow and a deep copy? How do you deep copy?
- What does
structuredClonenot support?