Lập trình · 19/10/2026

JavaScript structuredClone: Deep Copies and ArrayBuffer Ownership Transfer

One task needs an independent data copy; another needs ownership of a memory resource. structuredClone supports these different goals through cloning and transfer. Confusing them can leave code reading from a detached source buffer.

JavaScript structuredClone: sao chép sâu và chuyển quyền sở hữu ArrayBuffer

One task needs an independent data copy; another needs ownership of a memory resource. structuredClone supports these different goals through cloning and transfer. Confusing them can leave code reading from a detached source buffer.

The independent examples below require a runtime with global structuredClone. For nested references shared by object spread, see the shallow-copy article; this guide focuses on data types and resource lifetime.

1. Preserve supported data types

const source = {
  createdAt: new Date('2026-01-01T00:00:00Z'),
  counters: new Map([['views', 12]]),
  labels: new Set(['draft'])
};
const copy = structuredClone(source);
copy.counters.set('views', 20);

console.log(copy.createdAt instanceof Date); // true
console.log(copy.counters instanceof Map);   // true
console.log(copy.labels instanceof Set);    // true
console.log(source.counters.get('views'));   // 12

The copied Map changes independently. Specify which types the application must retain: a Date with getTime is different from a date string. JSON is a data interchange format; a JSON.stringify/JSON.parse round trip is not equivalent for arbitrary JavaScript values.

2. Cycles and shared relationships

const shared = { enabled: true };
const graph = { left: shared, right: shared };
graph.self = graph;

const cloned = structuredClone(graph);
console.log(cloned !== graph);             // true
console.log(cloned.self === cloned);       // true
console.log(cloned.left === cloned.right); // true
console.log(cloned.left === shared);       // false

A deep copy does not create a separate object for every path. Within the clone, left and right still reference the same cloned node. Changing cloned.left.enabled is visible through cloned.right.enabled, while the original shared node remains independent.

When taking a graph snapshot, relationships between nodes matter alongside their field values.

3. Cloning a buffer leaves its source usable

const original = new Uint8Array([10, 20, 30]);
const copied = structuredClone(original);
copied[0] = 99;

console.log(original[0]);         // 10
console.log(original.byteLength); // 3
console.log(copied[0]);           // 99

No transfer option is supplied. The copied memory is independent in this example. This fits workflows where both sides still need the data, subject to payload size and copying costs.

4. Transfer detaches the source buffer

const bytes = new Uint8Array([10, 20, 30]);
const moved = structuredClone(bytes, {
  transfer: [bytes.buffer]
});

console.log(bytes.byteLength); // 0
console.log(moved.byteLength); // 3
console.log(moved[0]);         // 10

The transfer list contains the typed array's underlying ArrayBuffer. The result receives the resource and the source buffer becomes detached. Other views backed by that source buffer are affected too.

A transfer list does not attach data to the result: the resource must also be reachable through the supplied value. Here, that value is the typed array itself.

For file processing, identify who owns the buffer after every step. When sending work through Worker.postMessage, follow that API's transfer contract. This example exercises structuredClone locally and does not benchmark Worker communication.

5. Functions and class behavior need explicit contracts

try {
  structuredClone({ run: () => 42 });
} catch (error) {
  console.log(error.name); // DataCloneError
}

Do not silently return the source object after a cloning failure: callers expecting independence would receive shared data. Separate callbacks from the payload and define plain data for component boundaries.

Cloning is not class reconstruction or input validation. For business objects with methods and invariants, define explicit export and reconstruction steps.

6. Before using it in a real workflow

  • Check availability in the target runtime.
  • List the types to preserve and unsupported values.
  • Test values, identities and reference relationships.
  • Clone when the source remains needed; transfer when ownership can be handed over.
  • Inspect every view sharing a buffer before transfer.
  • Measure time and memory with realistic payloads rather than cloning an entire state tree on every keystroke.

The snippets were executed locally with Node.js, covering Date/Map/Set, cycles, typed-array copying, transfer detachment and DataCloneError. Worker integration and browser performance were not tested.

References: structuredClone() and the structured clone algorithm. Choose the operation according to whether both sides need data or the next stage takes ownership.

Discussion

Comments 0

Sign in to comment

You need an account to join the discussion and reply to other readers.

Sign inRegister

No comments yet. Be the first to share your thoughts.