How structuredClone Handles Circular References
The native JavaScript structuredClone() function
seamlessly handles circular references by maintaining an internal
registry of previously cloned objects. Unlike legacy methods such as
JSON.parse(JSON.stringify()), which throw a
TypeError when encountering cyclical structures,
structuredClone() creates accurate, independent deep copies
while fully preserving complex object graph relationships and internal
references.
The Problem with Circular References
A circular reference occurs when an object references itself directly or indirectly through a chain of properties. For example:
const nodeA = { name: "Node A" };
const nodeB = { name: "Node B" };
nodeA.next = nodeB;
nodeB.prev = nodeA; // Circular referenceAttempting to serialize this structure with
JSON.stringify() results in an unhandled exception:
TypeError: Converting circular structure to JSON. Naive
recursive deep-clone implementations will typically enter an infinite
loop and trigger a
RangeError: Maximum call stack size exceeded.
The Internal Mechanism of structuredClone
The structuredClone() method uses the HTML standard
Structured Clone Algorithm. To resolve circular references, the
algorithm relies on an internal identity map (conceptually similar to a
WeakMap or hash table) throughout the cloning process:
- Traversal and Tracking: As the algorithm traverses the object tree, it records every encountered source object along with its newly created clone in an internal lookup table.
- Detection: Before cloning an object, the algorithm checks the lookup table to see if a clone for that specific reference has already been created.
- Reference Assignment: If a reference already exists
in the table,
structuredClone()stops deeper recursion on that branch and immediately assigns the existing clone reference to the target property. - Preserving Identity: If the object has not been seen before, it is duplicated, registered in the lookup table, and its properties are recursively cloned.
Code Example
const user = { name: "Alice" };
user.profile = { owner: user }; // Direct circular reference
const clonedUser = structuredClone(user);
// The clone is completely decoupled from the original
console.log(clonedUser !== user); // true
console.log(clonedUser.profile !== user.profile); // true
// The internal cyclic relationship is preserved in the clone
console.log(clonedUser.profile.owner === clonedUser); // truePreserving Complex Graph Topologies
In addition to self-referencing objects,
structuredClone() preserves shared sub-object topologies.
If two distinct properties in the original structure point to the exact
same object in memory, the cloned structure will mirror that
relationship—both cloned properties will point to a single shared
duplicate object, rather than creating two separate copies.
This behavior works across supported built-in types, including
standard objects, arrays, Map, and Set
instances.