JavaScript Deep Clone Object — structuredClone, JSON, and Alternatives | TryJS

Learn how to deep clone objects in JavaScript with structuredClone, JSON.parse/stringify, and manual recursion. Trade-offs and when to use each.

Overview

Deep cloning means creating a copy of an object where nested objects and arrays are also new, independent instances — not shared references. JavaScript has three common approaches: the built-in structuredClone() (modern, handles most cases), JSON.parse(JSON.stringify(obj)) (quick but lossy), and manual recursion (full control, tedious). Picking the right one depends on what your data contains.

How It Works

structuredClone() — the modern default

Built into Node 17+ and all modern browsers. Handles cyclic references, Maps, Sets, Dates, RegExps, typed arrays, and more. Does not copy functions, DOM nodes, or prototypes.

JSON.parse(JSON.stringify(obj)) — quick and dirty

One-liner that works for plain JSON-compatible data. Loses functions, undefined, Dates (become strings), Maps/Sets, Symbols, BigInt, and cyclic references (throws).

Spread or Object.assign — shallow only

{ ...obj } and Object.assign({}, obj) copy one level deep. Nested objects are still shared. Fine when you know the shape is flat.

Manual recursion or libraries

When you need to preserve classes, prototypes, or apply custom logic, a library like lodash.cloneDeep — or hand-written recursion — is the only option.

Common Mistakes

When to Use It

Reach for structuredClone first — it handles almost everything and has no dependencies. Fall back to JSON only for strictly-JSON data when you need the ~2x speedup. Use a library or hand-rolled recursion only when you need to preserve class instances or apply custom transformations.

Runnable Example

const original = {
  name: "Alice",
  joined: new Date("2024-01-15"),
  tags: new Set(["admin", "beta"]),
  meta: { score: 99, nested: { deep: true } },
};

// 1. structuredClone — preserves Date, Set, deep nesting
const clone1 = structuredClone(original);
clone1.meta.nested.deep = false;
console.log("original still true?", original.meta.nested.deep);
console.log("date preserved?", clone1.joined instanceof Date);
console.log("set preserved?", clone1.tags instanceof Set);

// 2. JSON clone — loses Set and converts Date
const clone2 = JSON.parse(JSON.stringify(original));
console.log("JSON clone date type:", typeof clone2.joined); // string
console.log("JSON clone tags:", clone2.tags); // {} (Set becomes empty object)

// 3. Spread is shallow
const clone3 = { ...original };
clone3.meta.score = 0;
console.log("spread is shallow — original score:", original.meta.score);

Open this example in the TryJS playground to edit and run the code instantly in your browser — no signup needed.

Frequently Asked Questions

What is the best way to deep clone an object in JavaScript?

In modern environments (Node 17+, all evergreen browsers), use structuredClone(obj). It handles nested objects, arrays, Maps, Sets, Dates, RegExps, and cyclic references with zero dependencies.

Why does JSON.parse(JSON.stringify(obj)) fail for some objects?

JSON only supports plain objects, arrays, strings, numbers, booleans, and null. It drops functions, undefined, and Symbols, converts Dates to strings, and throws on cyclic references. Use structuredClone or a library for anything richer.

Is spread ({ ...obj }) a deep clone?

No. Spread is always shallow: top-level keys get new references, but nested objects and arrays are still shared with the original. Modifying nested data will leak back to the source.

Does structuredClone copy class instances?

No — it creates plain objects. The prototype chain is lost, so the result is not instanceof your class. If you need that, use a library that handles prototypes or write a custom clone method.