Learn how to deep clone objects in JavaScript with structuredClone, JSON.parse/stringify, and manual recursion. Trade-offs and when to use each.
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.
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.
One-liner that works for plain JSON-compatible data. Loses functions, undefined, Dates (become strings), Maps/Sets, Symbols, BigInt, and cyclic references (throws).
{ ...obj } and Object.assign({}, obj) copy one level deep. Nested objects are still shared. Fine when you know the shape is flat.
When you need to preserve classes, prototypes, or apply custom logic, a library like lodash.cloneDeep — or hand-written recursion — is the only option.
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.
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.
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.
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.
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.
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.