JavaScript Interview Prep
Objects & Arrays

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 somehow original.address.city changed 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 how structuredClone finally 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


Shallow Copy vs Deep Copy thumbnail


What You'll Learn

  • Why spread and Object.assign produce 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 structuredClone works, 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

Shallow Copy vs Deep Copy visual 1


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 structuredClone as 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]) and Object.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 and undefined (dropped entirely), NaN and Infinity (become null), and circular references (throws TypeError).

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.

On this page