Blog

structuredClone vs JSON.parse(JSON.stringify()) in JavaScript

JSON.parse(JSON.stringify(obj)) is the old way to deep copy an object. It works until the object contains something JSON has no syntax for.

structuredClone is a built in function that does the same job properly. It is available in browsers and in Node 17 and later.

What the JSON round trip loses

Here is one object with a Date, a Set, a Map, a typed array, an undefined value and a BigInt. The BigInt is removed first because JSON.stringify throws on it.

const source = {
  when: new Date('2020-01-01T00:00:00Z'),
  tags: new Set(['a', 'b']),
  lookup: new Map([['k', 'v']]),
  bytes: new Uint8Array([1, 2, 3]),
  nested: { deep: { value: 1 } },
  missing: undefined,
  big: 10n
};

// the JSON round trip loses most of it
const viaJson = JSON.parse(JSON.stringify({ ...source, big: undefined }));
console.log('date  ->', typeof viaJson.when, viaJson.when);
console.log('set   ->', viaJson.tags);
console.log('map   ->', viaJson.lookup);
console.log('bytes ->', viaJson.bytes);
console.log('keys  ->', Object.keys(viaJson));

It prints:

date  -> string 2020-01-01T00:00:00.000Z
set   -> {}
map   -> {}
bytes -> { '0': 1, '1': 2, '2': 3 }
keys  -> [ 'when', 'tags', 'lookup', 'bytes', 'nested' ]

The Date came back as a string. The Set and the Map came back as empty objects, because neither has a JSON form. The typed array turned into a plain object with numeric keys. The undefined key vanished from the output entirely.

structuredClone keeps the types

The same object through structuredClone.

// structuredClone keeps the types
const clone = structuredClone(source);
console.log('date  ->', clone.when instanceof Date, clone.when.toISOString());
console.log('set   ->', clone.tags instanceof Set, [...clone.tags]);
console.log('map   ->', clone.lookup instanceof Map, [...clone.lookup]);
console.log('bytes ->', clone.bytes instanceof Uint8Array, [...clone.bytes]);
console.log('bigint ->', clone.big === 10n);
console.log('deep copy ->', clone.nested !== source.nested);

It prints:

date  -> true 2020-01-01T00:00:00.000Z
set   -> true [ 'a', 'b' ]
map   -> true [ [ 'k', 'v' ] ]
bytes -> true [ 1, 2, 3 ]
bigint -> true
deep copy -> true

Every type survived, and nested objects are real copies rather than shared references.

Cycles

An object that refers to itself cannot be stringified at all.

// cycles are fine
const cyclic = { name: 'root' };
cyclic.self = cyclic;
try { JSON.stringify(cyclic); }
catch (err) { console.log('JSON  ->', err.constructor.name); }
const cloned = structuredClone(cyclic);
console.log('clone ->', cloned.self === cloned);

It prints:

JSON  -> TypeError
clone -> true

The clone rebuilds the cycle. The copy points at the copy, not at the original.

What it will not do

Functions cannot be cloned. Neither can DOM nodes, and class instances lose their prototype.

// what it cannot clone
try { structuredClone({ fn: () => 1 }); }
catch (err) { console.log('function ->', err.constructor.name + ':', err.message); }

// class instances lose their prototype
class Point { constructor(x, y) { this.x = x; this.y = y; } sum() { return this.x + this.y; } }
const p = structuredClone(new Point(1, 2));
console.log(p, '| still a Point?', p instanceof Point);

It prints:

function -> DOMException: () => 1 could not be cloned.
{ x: 1, y: 2 } | still a Point? false

The data is copied, the identity is not. If you need methods back, pass the plain object to your constructor after cloning.

What to remember

  • The JSON round trip only preserves what JSON can express.
  • structuredClone keeps Date, Map, Set, RegExp, typed arrays and BigInt.
  • It handles cycles, which JSON.stringify refuses.
  • It cannot clone functions, and class instances come back as plain objects.

The same algorithm is what postMessage uses to send data to a worker, so the rules about what can cross a worker boundary and what can be cloned are the same rules.

How useful was this post?

Click on a heart to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Leave a Reply