Skip to content

/commands/javascript/deep-clone-object

javascript deep clone an object with structuredClone()

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · v24.20.0updated
tool
JavaScript
versions
Node.js 17+, Chrome 98+, Firefox 94+, Safari 15.4+
os
any

structuredClone(value) returns a deep copy of a value: nested objects and arrays are copied too, so changing the copy never touches the original. It is a global function in every current browser and in Node.js 17+, needs no library, and also copies Date, Map, Set, typed arrays and circular references.

Shallow copy vs deep copy

bash
cat > clone.mjs <<'EOF'
const original = {
  name: "config",
  created: new Date("2026-01-02T03:04:05Z"),
  tags: new Set(["a", "b"]),
  nested: { retries: 3 },
};
original.self = original; // circular reference

const shallow = { ...original };
const deep = structuredClone(original);

shallow.nested.retries = 10;
console.log("after shallow edit:", original.nested.retries);
deep.nested.retries = 99;
console.log("after deep edit:", original.nested.retries);
console.log("Date kept:", deep.created instanceof Date, deep.created.toISOString());
console.log("Set kept:", deep.tags instanceof Set, [...deep.tags]);
console.log("circular kept:", deep.self === deep);
EOF
node clone.mjs

Output

after shallow edit: 10
after deep edit: 10
Date kept: true 2026-01-02T03:04:05.000Z
Set kept: true [ 'a', 'b' ]
circular kept: true

The spread copy shares nested with the original, so its edit leaks back; the structuredClone copy does not. The Date stays a Date, the Set stays a Set, and the circular reference points at the new object.

What cannot be cloned

Functions, DOM nodes and some host objects make structuredClone throw a DataCloneError. Class instances lose their prototype and become plain objects; property getters are copied as their current values.

bash
cat > fail.mjs <<'EOF'
class Point { constructor(x) { this.x = x; } norm() { return Math.abs(this.x); } }
const copy = structuredClone(new Point(-2));
console.log("class instance:", copy instanceof Point, copy);
try {
  structuredClone({ onClick() {} });
} catch (e) {
  console.log(e.name + ":", e.message);
}
EOF
node fail.mjs

Output

class instance: false { x: -2 }
DataCloneError: onClick() {} could not be cloned.

Ways to copy an object

MethodDepthNotes
structuredClone(obj)DeepKeeps Date, Map, Set, RegExp, typed arrays, circular references; throws on functions
{ ...obj }ShallowFast; nested objects are shared with the original
Object.assign({}, obj)ShallowSame as spread, also triggers setters on the target
JSON.parse(JSON.stringify(obj))DeepPlain data only: Dates become strings, undefined and functions are dropped
arr.slice() / [...arr]ShallowCopies an array; the elements themselves are shared
Lodash _.cloneDeep(obj)DeepLibrary function; keeps prototypes for many types

Pitfalls

  • Old runtimes. On Node.js 16 and older, structuredClone is missing and the call fails with ReferenceError: structuredClone is not defined (see x is not defined). Upgrade Node or use the JSON round trip for plain data.
  • Lost methods. A cloned class instance has no methods; calling one fails with x is not a function. Re-create the instance from the copied data if you need behaviour.
  • Hand-written recursive copies of deeply nested or circular data can overflow the stack; structuredClone handles cycles for you.

Confirm it worked

Change a nested field on the copy and print the original: if the original is unchanged, the copy is deep. copy !== original alone proves nothing, because a shallow copy is also a new object.

FAQ

Is the spread operator a deep copy?

No. { ...obj } and Object.assign({}, obj) copy only the top level. Nested objects and arrays are shared between the original and the copy, so changing copy.nested.x also changes obj.nested.x.

Why does structuredClone throw DataCloneError?

The value contains something the structured clone algorithm cannot copy: a function, a DOM node, a symbol-keyed value or an object such as a WeakMap. Remove those properties first or copy the object by hand.

Is JSON.parse(JSON.stringify(obj)) still useful?

Only for plain JSON-like data. It turns Dates into strings, drops undefined values and functions, turns NaN and Infinity into null, and throws on circular references. structuredClone has none of those problems.

Sources