|$ curl https://forge-ai.dev/api/markdown?path=docs/js/structured-clone
$cat docs/structured-clone-algorithm.md
updated Last week·20 min read·published

Structured Clone Algorithm

JavaScriptData StructuresSerializationIntermediate🎯Free Tools
Introduction

The structuredClone()algorithm is JavaScript's built-in way to create deep copies of values. It was standardized in ES2022 and is now supported in all modern browsers and Node.js 17+. Before structuredClone, developers relied on JSON.parse(JSON.stringify()) or manual recursive copying — both of which had significant limitations.

The structured clone algorithm traverses the input value recursively, creating a deep copy of each property. Unlike JSON serialization, it handles circular references, typed arrays, Date, RegExp, Map, Set, Blob, File, ArrayBuffer, and other complex types. It is also faster than manual deep copy implementations in most engines.

structured-clone-intro.js
JavaScript
1// Shallow copy — shares nested references
2const original = { name: "Alice", scores: [95, 87, 92] };
3const shallow = { ...original };
4
5shallow.name = "Bob";
6shallow.scores.push(100);
7
8console.log(original.name); // "Alice" — unchanged (primitive)
9console.log(original.scores); // [95, 87, 92, 100] — mutated! (reference)
10
11// Deep copy — independent nested objects
12const deep = structuredClone(original);
13
14deep.name = "Charlie";
15deep.scores.push(75);
16
17console.log(original.name); // "Alice" — unchanged
18console.log(original.scores); // [95, 87, 92, 100] — unchanged
Basic Usage

The structuredClone() function takes a value and returns a deep copy. It works with any serializable JavaScript value — primitives, objects, arrays, dates, maps, sets, typed arrays, and more.

structured-clone-basic.js
JavaScript
1// Basic deep clone
2const user = {
3 name: "Alice",
4 age: 30,
5 address: {
6 city: "Portland",
7 state: "OR",
8 zip: "97201"
9 },
10 hobbies: ["reading", "hiking"]
11};
12
13const clone = structuredClone(user);
14
15clone.name = "Bob";
16clone.address.city = "Seattle";
17clone.hobbies.push("coding");
18
19console.log(user.name); // "Alice" — unchanged
20console.log(user.address.city); // "Portland" — unchanged
21console.log(user.hobbies); // ["reading", "hiking"] — unchanged
22console.log(clone.name); // "Bob"
23console.log(clone.address.city); // "Seattle"
24console.log(clone.hobbies); // ["reading", "hiking", "coding"]
25
26// Works with primitives
27console.log(structuredClone(42)); // 42
28console.log(structuredClone("hello")); // "hello"
29console.log(structuredClone(null)); // null
30console.log(structuredClone(undefined)); // undefined

info

structuredClone() is significantly faster than JSON.parse(JSON.stringify()) in most JavaScript engines because it avoids the string conversion step. It is also more correct — JSON cannot handle circular references, Date objects, or typed arrays.
Handling Special Objects

The structured clone algorithm handles many built-in JavaScript types that JSON serialization cannot. This makes it suitable for cloning complex data structures that include dates, regular expressions, maps, sets, typed arrays, and more.

structured-clone-special.js
JavaScript
1// Date objects — cloned correctly
2const original = new Date("2024-01-15");
3const cloned = structuredClone(original);
4console.log(cloned instanceof Date); // true
5console.log(cloned.getTime() === original.getTime()); // true
6
7// RegExp objects — cloned correctly
8const pattern = /hello/gi;
9const clonedPattern = structuredClone(pattern);
10console.log(clonedPattern instanceof RegExp); // true
11console.log(clonedPattern.source); // "hello"
12console.log(clonedPattern.flags); // "gi"
13
14// Map objects — cloned correctly
15const map = new Map([["key1", "value1"], ["key2", "value2"]]);
16const clonedMap = structuredClone(map);
17console.log(clonedMap instanceof Map); // true
18console.log(clonedMap.get("key1")); // "value1"
19console.log(clonedMap !== map); // true — different reference
20
21// Set objects — cloned correctly
22const set = new Set([1, 2, 3, 4, 5]);
23const clonedSet = structuredClone(set);
24console.log(clonedSet instanceof Set); // true
25console.log(clonedSet.size); // 5
26console.log(clonedSet !== set); // true — different reference
27
28// Typed arrays — cloned correctly
29const arr = new Int32Array([1, 2, 3, 4]);
30const clonedArr = structuredClone(arr);
31console.log(clonedArr instanceof Int32Array); // true
32console.log(clonedArr.buffer !== arr.buffer); // true — different ArrayBuffer
structured-clone-buffer.js
JavaScript
1// ArrayBuffer and DataView
2const buffer = new ArrayBuffer(16);
3const view = new DataView(buffer);
4view.setUint8(0, 42);
5view.setFloat32(4, 3.14);
6
7const clonedBuffer = structuredClone(buffer);
8const clonedView = new DataView(clonedBuffer);
9
10console.log(clonedView.getUint8(0)); // 42
11console.log(clonedView.getFloat32(4)); // 3.14
12console.log(clonedBuffer !== buffer); // true — different ArrayBuffer
13
14// Blob and File (browser)
15const blob = new Blob(["Hello"], { type: "text/plain" });
16const clonedBlob = structuredClone(blob);
17console.log(clonedBlob instanceof Blob); // true
18
19// Circular references — handled automatically
20const circular = { name: "root" };
21circular.self = circular; // circular reference
22
23const clonedCircular = structuredClone(circular);
24console.log(clonedCircular.self === clonedCircular); // true — preserved
25console.log(clonedCircular !== circular); // true — different object
Limitations

Despite its power, structuredClone has specific limitations. Some JavaScript types cannot be cloned because they contain non-serializable state like functions, DOM nodes, class instances with private fields, or WeakMap/WeakSet references.

structured-clone-limitations.js
JavaScript
1// These will throw DataCloneError:
2// 1. Functions
3try {
4 structuredClone(() => {});
5} catch (e) {
6 console.error(e.message); // "Failed to execute 'structuredClone'"
7}
8
9// 2. DOM nodes
10try {
11 structuredClone(document.body);
12} catch (e) {
13 console.error(e.message); // "Failed to execute 'structuredClone'"
14}
15
16// 3. WeakMap and WeakSet (weak references can't be cloned)
17try {
18 structuredClone(new WeakMap());
19} catch (e) {
20 console.error(e.message);
21}
22
23// 4. Symbols (unique identity can't be cloned)
24try {
25 structuredClone({ key: Symbol("test") });
26} catch (e) {
27 console.error(e.message);
28}
29
30// 5. Class instances with methods (methods are not cloned)
31class Animal {
32 constructor(name) { this.name = name; }
33 speak() { return `${this.name} makes a noise`; }
34}
35
36const lion = new Animal("Leo");
37const clonedLion = structuredClone(lion);
38
39console.log(clonedLion.name); // "Leo" — data cloned
40console.log(clonedLion.speak); // undefined — methods lost!
41console.log(clonedLion instanceof Animal); // false — wrong prototype

warning

structuredClone() does not clone methods, prototypes, getters/setters, or constructor functions. It clones the data of an object, not its behavior. For class instances, you will need a custom cloning strategy that preserves the prototype chain.
Alternatives & Comparison

Before structuredClone, developers used several approaches for deep copying. Each has trade-offs. Here is a comparison of the most common methods.

structured-clone-alternatives.js
JavaScript
1// Method 1: JSON.parse(JSON.stringify())
2// Limitations: no Date, no RegExp, no Map/Set, no circular refs, no undefined
3const jsonClone = JSON.parse(JSON.stringify(original));
4
5// Method 2: Recursive deep clone
6function deepClone(obj, seen = new WeakMap()) {
7 if (obj === null || typeof obj !== "object") return obj;
8 if (seen.has(obj)) return seen.get(obj);
9
10 if (obj instanceof Date) return new Date(obj);
11 if (obj instanceof RegExp) return new RegExp(obj);
12 if (obj instanceof Map) {
13 const map = new Map();
14 seen.set(obj, map);
15 obj.forEach((v, k) => map.set(deepClone(k, seen), deepClone(v, seen)));
16 return map;
17 }
18 if (obj instanceof Set) {
19 const set = new Set();
20 seen.set(obj, set);
21 obj.forEach(v => set.add(deepClone(v, seen)));
22 return set;
23 }
24
25 const clone = Array.isArray(obj) ? [] : {};
26 seen.set(obj, clone);
27 for (const key of Reflect.ownKeys(obj)) {
28 clone[key] = deepClone(obj[key], seen);
29 }
30 return clone;
31}
32
33// Method 3: structuredClone (recommended)
34const clone = structuredClone(original);
35
36// Comparison:
37// JSON.parse/stringify: fast but loses types, no circular refs
38// recursive deepClone: flexible but slower, custom code to maintain
39// structuredClone: fast, correct, built-in, handles most cases

best practice

Use structuredClone() as your default deep copy method. Only fall back to custom cloning when you need to preserve class instances, functions, or DOM nodes. For most data-focused cloning, structuredClone is faster and more correct than any alternative.
Best Practices
Use structuredClone() for deep copies — it is faster and more correct than JSON.parse(JSON.stringify())
Avoid deep copying large objects when shallow copy suffices — structuredClone traverses the entire graph
For class instances, implement a clone() method that preserves the prototype chain
Use structuredClone() with Map and Set — JSON serialization loses these types
Handle circular references — structuredClone supports them automatically
Do not use structuredClone for DOM nodes or functions — it will throw DataCloneError
Consider immutability libraries (Immer, Immutable.js) for complex state management scenarios
$Blueprint — Engineering Documentation·Section ID: JS-SCLONE·Revision: 1.0