Shallow Copy vs Deep Copy
The Photocopy Trap
LinkedIn Hook
You wrote
const copy = { ...original }and felt safe.Then you updated
copy.address.city— and somehoworiginal.address.citychanged too. Your state container now leaks mutations across components and nobody knows why.That's the shallow copy trap, and it catches developers in production far more often than in interviews.
{ ...obj },Object.assign({}, obj), and[...arr]only clone the top level. Every nested object or array inside is still the same reference shared with the original. Touch it on the copy, you touch it on the source.In this lesson you'll learn exactly when shallow breaks, why
JSON.parse(JSON.stringify(x))silently destroys your Date objects, functions, and circular references, and howstructuredClonefinally solves the problem — along with the three things it still refuses to clone.If you've ever debugged a "this state should not be changing" bug, this lesson is for you.
Read the full lesson -> [link]
#JavaScript #DeepCopy #StructuredClone #InterviewPrep #Frontend #WebDevelopment #CodingInterview
What You'll Learn
- Why spread and
Object.assignproduce shallow copies and where they break - The exact failures of
JSON.parse(JSON.stringify(x))for Date, functions, undefined, NaN, Map, Set, and circular references - How
structuredCloneworks, what it preserves, and the three categories it still cannot clone
The Photocopy Analogy
Think of copying an object like photocopying a document. A shallow copy photocopies the first page, but for any attached documents (nested objects), it just copies the staple — both the original and the copy point to the same attached pages. A deep copy photocopies everything, including all attached documents, creating fully independent copies.
What "Shallow" Really Means
A shallow copy creates a new object and copies all top-level properties. But if a property's value is a reference type (object, array), it copies the reference, not the actual data.
const original = {
name: "Rakibul",
scores: [90, 85, 92],
address: { city: "Dhaka", zip: "1200" }
};
// Shallow copy with spread
const shallow = { ...original };
shallow.name = "Karim"; // independent — primitive
console.log(original.name); // "Rakibul" (unchanged)
shallow.address.city = "Chittagong"; // shared reference!
console.log(original.address.city); // "Chittagong" (CHANGED!)
shallow.scores.push(100); // shared reference!
console.log(original.scores); // [90, 85, 92, 100] (CHANGED!)
Shallow Copy Methods
const obj = { a: 1, b: { c: 2 } };
// Method 1: Spread operator
const copy1 = { ...obj };
// Method 2: Object.assign
const copy2 = Object.assign({}, obj);
// Method 3: Array spread (for arrays)
const arr = [1, [2, 3]];
const arrCopy = [...arr];
// All three are shallow — nested references are shared
copy1.b.c = 99;
console.log(obj.b.c); // 99 — original mutated!
Deep Copy — JSON Method (Limited)
const original = {
name: "Rakibul",
address: { city: "Dhaka" }
};
const deep = JSON.parse(JSON.stringify(original));
deep.address.city = "Chittagong";
console.log(original.address.city); // "Dhaka" (unchanged!)
Where JSON deep copy FAILS:
const problematic = {
date: new Date("2025-01-01"), // becomes a string
regex: /hello/gi, // becomes {}
fn: function() { return 42; }, // disappears entirely
undef: undefined, // disappears entirely
nan: NaN, // becomes null
infinity: Infinity, // becomes null
map: new Map([["key", "value"]]), // becomes {}
set: new Set([1, 2, 3]) // becomes {}
};
const jsonCopy = JSON.parse(JSON.stringify(problematic));
console.log(jsonCopy);
// {
// date: "2025-01-01T00:00:00.000Z", // string, not Date!
// regex: {},
// nan: null,
// infinity: null,
// map: {},
// set: {}
// // fn and undef are GONE
// }
Circular references crash it:
const circular = { name: "Rakibul" };
circular.self = circular;
JSON.parse(JSON.stringify(circular));
// TypeError: Converting circular structure to JSON
Deep Copy — structuredClone (Modern Solution)
const original = {
date: new Date("2025-01-01"),
nested: { deep: { value: 42 } },
arr: [1, [2, 3]],
map: new Map([["key", "value"]]),
set: new Set([1, 2, 3])
};
const deep = structuredClone(original);
deep.nested.deep.value = 100;
console.log(original.nested.deep.value); // 42 (unchanged!)
console.log(deep.date instanceof Date); // true (preserved!)
console.log(deep.map.get("key")); // "value" (preserved!)
console.log(deep.set.has(2)); // true (preserved!)
// Handles circular references too!
const circular = { name: "Rakibul" };
circular.self = circular;
const cloned = structuredClone(circular); // works fine!
console.log(cloned.self === cloned); // true
What structuredClone CANNOT handle:
const obj = {
fn: () => 42, // functions — throws error
symbol: Symbol("id"), // symbols — throws error
dom: document.body // DOM nodes — throws error
};
structuredClone(obj); // DOMException: could not be cloned
Common Mistakes
- Assuming
{ ...obj }deep clones — it only copies the first level, every nested object is still shared by reference. - Reaching for
JSON.parse(JSON.stringify(x))on data containing Dates, functions,undefined,NaN,Map,Set, or cycles — you'll silently lose or corrupt those values. - Treating
structuredCloneas a total replacement — it throws on functions, symbols, and DOM nodes, so know what's in your object before you clone it.
Interview Questions
Q: What is the difference between shallow copy and deep copy?
A shallow copy creates a new object but copies references for nested objects — both copies share the same nested data. A deep copy recursively copies everything, creating completely independent objects at every level.
Q: Why does spread operator fail for deep copying?
Spread only copies the first level of properties. If a property holds a reference (object/array), spread copies the reference pointer, not the actual data. Mutating nested properties on the copy will affect the original.
Q: Compare JSON.parse/stringify vs structuredClone for deep copying.
JSON method loses Date objects (converts to strings), drops undefined/functions/symbols, fails on circular references, and cannot handle Map/Set. structuredClone preserves all these types and handles circular references, but cannot clone functions, symbols, or DOM nodes.
Q: What is a shallow copy? What methods create one?
A shallow copy is a new object where top-level primitives are copied by value but nested objects/arrays are still shared by reference. The common creators are the spread operator (
{ ...obj },[...arr]) andObject.assign({}, obj).
Q: Where does JSON.parse(JSON.stringify()) fail for deep copy?
On
Date(becomes a string),RegExp/Map/Set(become empty objects), functions andundefined(dropped entirely),NaNandInfinity(becomenull), and circular references (throwsTypeError).
Q: What is structuredClone? What can't it clone?
A built-in deep clone using the structured clone algorithm. It preserves Date, Map, Set, RegExp, typed arrays, ArrayBuffer, and circular references. It throws on functions, symbols, and DOM nodes, and it does not preserve prototype chains of custom class instances.
Quick Reference — Cheat Sheet
SHALLOW vs DEEP COPY — QUICK MAP
Shallow (top-level only, nested refs shared):
{ ...obj }
Object.assign({}, obj)
[...arr]
Deep — JSON trick (loses a lot):
JSON.parse(JSON.stringify(obj))
Drops: functions, undefined
Mangles: Date -> string, NaN/Infinity -> null
Wipes: Map, Set, RegExp -> {}
Crashes: circular references
Deep — structuredClone (modern default):
structuredClone(obj)
Preserves: Date, Map, Set, RegExp, typed arrays, cycles
Throws on: functions, symbols, DOM nodes
Rule of thumb:
need a safe deep copy of plain data -> structuredClone
need a shallow merge of configs -> spread
Previous: Generator Functions -> Pausable, Resumable Functions Next: Spread & Rest Operators -> Same Syntax, Opposite Directions
This is Lesson 7.1 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.