deep-clone
javascript
clone

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

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In modern JavaScript, the best general-purpose answer is usually structuredClone(...). It is built into current runtimes, handles many more data types than the old JSON trick, and avoids a lot of bugs that appear when people try to write their own recursive deep-clone logic.

What “Deep Clone” Actually Means

A deep clone creates a new object graph, not just a new top-level object. That means nested arrays and nested objects are copied instead of shared.

For example, this is only a shallow copy:

javascript
1const original = { user: { name: "Ada" } };
2const shallow = { ...original };
3
4shallow.user.name = "Grace";
5
6console.log(original.user.name);

That prints "Grace" because the nested user object was still shared.

A deep clone should avoid that by duplicating the nested structure as well.

Use structuredClone First

If the runtime supports it, structuredClone is the cleanest built-in choice:

javascript
1const original = {
2  id: 1,
3  tags: ["new", "sale"],
4  meta: { active: true },
5};
6
7const copy = structuredClone(original);
8
9copy.meta.active = false;
10copy.tags.push("featured");
11
12console.log(original);
13console.log(copy);

This creates an independent copy of the nested data.

structuredClone is stronger than JSON.parse(JSON.stringify(...)) because it supports more kinds of values and can handle circular references:

javascript
1const original = { name: "node" };
2original.self = original;
3
4const copy = structuredClone(original);
5
6console.log(copy !== original);
7console.log(copy.self === copy);

That works, while the JSON approach would fail on the circular reference.

When JSON Cloning Is Still Acceptable

The old JSON pattern still appears everywhere:

javascript
const copy = JSON.parse(JSON.stringify(original));

It is simple, and for plain JSON-compatible data it can be fine. But it has important limits:

  • it drops functions
  • it loses undefined
  • it converts Date objects to strings
  • it cannot handle Map, Set, or circular references

So the JSON trick is not “wrong.” It is just narrower than many examples admit. Use it only when the data is truly plain JSON data and you want a quick compatibility-oriented solution.

Know What structuredClone Does Not Clone

structuredClone is not magic. It is great for structured data, but some values are not cloneable in the way people expect.

For example, functions are not deep-cloned as executable copies of behavior. DOM-related objects and special class instances may also behave differently than a plain-object copy.

That means deep cloning is often the wrong operation when your data includes:

  • class instances with methods
  • open sockets or live resources
  • framework-managed objects
  • closures or functions

In those cases, a dedicated conversion function or domain-specific copy method is usually more correct than a generic deep clone.

Performance Depends on Data Shape

The “most efficient” method depends on what you are cloning.

For ordinary structured data in modern environments:

  • 'structuredClone is usually the best default'
  • JSON cloning can be fast enough for plain data but loses type information
  • custom recursive cloners often become bug farms unless you truly need special behavior

The biggest performance mistake is usually not choosing the wrong cloning function. It is cloning far more data than the application actually needs.

Often the real solution is:

  • clone only the branch you need
  • use immutable update patterns
  • avoid cloning large trees on every render or request

If your application deep-clones huge objects repeatedly in a hot path, the design may need revision more than the cloning function does.

A Safe Fallback Strategy

If you need broad runtime support, you can use structuredClone when available and fall back to JSON for plain data:

javascript
1function cloneData(value) {
2  if (typeof structuredClone === "function") {
3    return structuredClone(value);
4  }
5
6  return JSON.parse(JSON.stringify(value));
7}
8
9const original = { items: [1, 2, 3], ok: true };
10const copy = cloneData(original);
11
12console.log(copy);

This is a practical fallback only when you know the non-structuredClone case will receive JSON-safe data.

When a Library Still Makes Sense

If the project already uses a utility library with a tested deep-clone function, that can still be fine. Libraries can help when:

  • runtime support is mixed
  • the application already depends on that utility package
  • the team wants consistent behavior across environments

But do not add a dependency automatically if a built-in solution already covers the real use case.

Common Pitfalls

The most common pitfall is using object spread or Object.assign and assuming the result is a deep clone. Those only copy the first level.

Another mistake is using the JSON trick on data containing dates, maps, sets, circular references, or undefined, then wondering why values disappeared or changed type.

A third issue is deep cloning framework objects or class instances that were never meant to be copied generically. Structured data is a better target for cloning than behavior-rich objects.

Finally, developers often ask for the “fastest deep clone” when the bigger problem is unnecessary cloning. Reducing clone frequency can matter more than micro-optimizing the method.

Summary

  • In modern JavaScript, structuredClone is usually the best default deep-clone option.
  • It handles nested data and circular references better than the JSON trick.
  • 'JSON.parse(JSON.stringify(...)) is only safe for plain JSON-compatible data.'
  • Shallow copies such as spread syntax do not clone nested objects.
  • If deep cloning is a hot-path bottleneck, reconsider the data-flow design as well as the cloning method.

Course illustration
Course illustration

All Rights Reserved.