HuntExams Academy logo
JavaScriptIntermediate#objects

What is the difference between shallow copy and deep copy?

Answer

A shallow copy duplicates the top level while nested references stay shared, so mutating a nested value affects both. A deep copy recursively duplicates everything.

Example

const a = { n: { v: 1 } };
const s = { ...a };
s.n.v = 9;
a.n.v; // 9 — shared
const d = structuredClone(a);

Detailed explanation

In JavaScript, the difference between shallow and deep copying is whether nested objects remain shared. Objects, including arrays, are reference values: assigning an object to another variable copies its reference, not the object itself. A shallow copy creates a new outer object and copies its property values. Primitive values such as numbers and strings are copied as values, while properties containing objects still refer to the original nested objects. This matters when updating application state, transforming API responses, or passing data between functions without unintentionally changing the source.

For example, with `const a = { n: { v: 1 } }; const s = { ...a };`, `s !== a`, but `s.n === a.n`. Therefore, `s.n.v = 9` also makes `a.n.v` equal to `9`. However, assigning `s.n = { v: 5 }` replaces only the copied object's property and leaves `a.n` unchanged. Object spread and `Object.assign({}, a)` produce shallow object copies; array spread, `slice()`, and `Array.from()` similarly produce shallow array copies. Object spread copies own enumerable properties, not the source object's prototype or property descriptors.

A deep copy creates independent copies of nested mutable data, so changing a copied nested object does not change the original. For supported values, `const d = structuredClone(a)` provides this behaviour: `d.n !== a.n`. If called after the earlier mutation, it copies the current value, `9`, not the initial value, `1`. The structured clone algorithm tracks previously visited objects, allowing circular references and preserving repeated references within the cloned graph. It supports types such as `Date`, `Map`, `Set`, and typed arrays, but cannot clone functions and does not preserve custom class prototypes.

Deep copying is therefore not simply 'recursively duplicate everything' for every possible JavaScript value. `JSON.parse(JSON.stringify(value))` is only suitable for JSON-compatible data: it fails on cycles, converts dates to strings, and loses some values. Interviewers expect you to explain identity, predict mutation results, and choose an appropriate strategy. Deep copying generally requires more work and memory; for a small immutable update, copying only the changed path is often preferable.

Key points to remember

  • Assignment shares the original object; a shallow copy creates a distinct outer object.
  • Shallow copies retain references to nested objects and arrays.
  • Mutating a shared nested object affects both copies; replacing a top-level property does not.
  • `structuredClone()` deeply clones supported data, including circular references, but not functions.
  • Choose copying based on data types and update requirements; deep cloning is not always necessary.

Common mistakes candidates make

  • Assuming object spread or `Object.assign()` makes nested objects independent.
  • Treating JSON serialization as a universal deep-copy solution.
  • Claiming a deep copy preserves every JavaScript value, custom prototype, and property descriptor.

Likely follow-up questions

How can you update a nested value without deep-cloning the entire object?

Copy each object along the path being changed: `const next = { ...a, n: { ...a.n, v: 9 } };`. This leaves `a.n.v` unchanged while allowing untouched branches to remain shared.

What happens when `structuredClone()` encounters circular references?

It handles cycles by tracking objects already encountered during cloning. For `a.self = a`, the clone's `self` points to the clone itself, not to `a`.

Why can JSON serialization produce an incorrect deep copy?

`JSON.stringify()` omits object properties containing `undefined`, functions, or symbols, and converts dates to strings. It also throws for circular references and, by default, `BigInt` values, so it is not a general-purpose cloning mechanism.

Interview tip: Use the nested-object example to compare identity, nested mutation, and property replacement before explaining deep-cloning limitations.

Related Interview Questions

1
JavaScriptBeginner#es6#strings

What are template literals?

Open
2
JavaScriptAdvanced#functions

What is currying?

Open
3
JavaScriptIntermediate#functions

What is a higher-order function?

Open