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 reference

Attempting 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:

  1. 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.
  2. Detection: Before cloning an object, the algorithm checks the lookup table to see if a clone for that specific reference has already been created.
  3. 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.
  4. 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); // true

Preserving 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.