What does the global structuredClone() function do, and how does the copy it produces differ from JSON.parse(JSON.stringify(value))?
answer
- deep copy, not a text round-trip
- rich types survive the trip
- cycles kept, functions rejected
- same rule as postMessage and IndexedDB
- Map, Set, Date, ArrayBuffer, Blob
basics
~20 sstructuredClone() returns a deep copy made by the browser's structured clone algorithm. Unlike a JSON round-trip it preserves Map, Set, Date, RegExp, typed arrays, Blobs and cyclic references, but it throws a DataCloneError on functions and DOM nodes.
solid answer
~50 s`structuredClone(value)` is a global that deep-copies a value using the structured clone algorithm — the same algorithm the browser already uses for `postMessage` and for values written to IndexedDB. It walks the object graph and rebuilds it, so nested objects are genuinely copied rather than shared. Compared with `JSON.parse(JSON.stringify(value))` it is much wider: `Date` stays a `Date` instead of becoming a string, `Map` and `Set` survive as `Map` and `Set` instead of collapsing to `{}`, `ArrayBuffer`, typed arrays, `Blob` and `File` are supported, and cycles are preserved instead of throwing. It is also stricter in one direction: anything the algorithm cannot serialize — a function, a `Symbol`, a DOM node, a `Promise` — throws a `DataCloneError` rather than being silently dropped the way `JSON.stringify` drops it. It has been available as a global in Chrome 98+, Firefox 94+ and Safari 15.4+ since 2022.
code
javascript · 14 linesconst src = {
when: new Date(0),
tags: new Set(['a', 'b']),
lookup: new Map([['k', 1]]),
big: 10n,
};
const clone = structuredClone(src);
console.log(clone.when instanceof Date, clone.tags.has('a'), clone.lookup.get('k'), clone.big);
// true true 1 10n
const viaJson = JSON.parse(JSON.stringify({ when: src.when, tags: src.tags, lookup: src.lookup }));
console.log(typeof viaJson.when, viaJson.tags, viaJson.lookup);
// "string" {} {}go deeper
Be able to say that structuredClone() gives a genuine deep copy and that a JSON round-trip loses Date, Map and Set and throws on cycles. Name at least one value kind that makes structuredClone throw.
Explain the cloneable set as a specification-defined list rather than a heuristic, and contrast loud refusal (DataCloneError) with JSON's silent data loss. Mention that prototypes and property descriptors do not survive.
Show that you recognise the same algorithm behind postMessage, IndexedDB writes and history state, so a value that fails one boundary will fail the others. Note that the clone is synchronous and its cost scales with graph size.
Own the message-and-storage contract: decide whether values crossing boundaries are plain transport DTOs or rich domain objects, and make that choice explicit so teams do not discover the clone rules one DataCloneError at a time.
## What the function is `structuredClone(value)` is a global function exposed on `Window` and on worker global scopes. It takes any value and returns a deep copy of it, produced by the **structured clone algorithm** — a serialize-then-deserialize pair defined by the HTML specification. The function is new (Chrome 98, Firefox 94, Safari 15.4, all shipped by early 2022), but the algorithm behind it is old: it is the rule the browser has always used to decide what may cross `postMessage`, what may be written into IndexedDB, and what may be handed to `history.pushState`. Learning it once explains all of those boundaries at the same time. ```js const copy = structuredClone(original); copy.nested.value = 'changed'; // original.nested.value is untouched ``` ## What "deep" actually means here The algorithm walks the whole reachable graph of the input. Every plain object and array is rebuilt, so mutating anything in the copy — at any depth — cannot be observed through the original. This is the property a shallow copy such as `{ ...obj }` or `Object.assign({}, obj)` does not give you: those copy the top-level properties, so a nested object is still the *same* object in both. ## What survives The cloneable set is broad and is defined by the specification, not by the function: - primitives, including `BigInt`, and boxed `String`/`Number`/`Boolean` objects - plain objects and arrays (including sparse arrays) - `Date`, `RegExp` (source and flags; `lastIndex` is not carried over) - `Map` and `Set`, with their keys and values cloned recursively - `ArrayBuffer`, all typed arrays and `DataView` - `Blob`, `File`, `FileList`, `ImageData`, `ImageBitmap` - `Error` and its standard subclasses (`name`, `message`, and `cause`) - cyclic and shared references, which are reproduced as cycles and shared references ## What a JSON round-trip does instead `JSON.parse(JSON.stringify(value))` is a text round-trip, and JSON's data model is far smaller than JavaScript's. A `Date` serializes through its `toJSON` method and comes back as a string. A `Map` or `Set` has no enumerable own properties, so it serializes as `{}` and the contents are simply gone. `undefined`, functions and symbol values are dropped from objects and turned into `null` inside arrays. `NaN` and `Infinity` become `null`. A cycle makes `JSON.stringify` throw. And a `BigInt` makes it throw as well. ```js const src = { when: new Date(0), tags: new Set(['a']) }; structuredClone(src).tags instanceof Set; // true JSON.parse(JSON.stringify(src)).tags; // {} ``` The practical difference is not that one is "better": it is that JSON's losses are *silent* while the clone algorithm's refusals are *loud*. ## Where structuredClone is stricter Anything outside the cloneable set throws a `DOMException` whose `name` is `"DataCloneError"`. Functions, symbols as values, DOM nodes, `Promise`, `WeakMap`/`WeakSet` and proxies all fail this way. So if your object carries a callback, `structuredClone` will not quietly hand you a copy missing that callback — it will refuse the whole operation. Two more things change quietly rather than throwing. Prototypes are not preserved: a class instance comes back as a plain object whose prototype is `Object.prototype`, so its methods are gone. And property descriptors are flattened: only own **enumerable string-keyed** properties are copied, a getter is invoked once and its result stored as an ordinary data property, and symbol-keyed properties are skipped. ## Choosing between them Use `structuredClone` when you want an in-memory deep copy and you control the shape of the value — it is faster than serializing to text and back, and it keeps the rich types. Use `JSON.stringify` when the destination genuinely needs *text*: an HTTP body, a log line, or `localStorage`, whose API only accepts strings. Reach for a library deep-clone only when you need behaviour the algorithm deliberately does not give you, such as preserving class prototypes or copying functions by reference. One last practical note: `structuredClone` is synchronous and runs on the calling thread, so on a very large graph it is not free — the cost is proportional to the size of the graph it has to walk.
- Other than calling structuredClone() directly, where else does the browser run this same algorithm?Anywhere a value crosses a boundary the browser controls: `postMessage` on a window, worker, `MessagePort` or `BroadcastChannel`; values written to IndexedDB; and the state object given to `history.pushState`/`replaceState`. `localStorage` is the odd one out — it stores strings only, which is why JSON is used there.
- If structuredClone is a deep copy, why does Object.assign or spread not do the same job?Both are shallow. `{ ...obj }` copies the top-level own enumerable properties, so any nested object is the *same* object in both the source and the copy — mutating `copy.nested.x` is visible through `original.nested.x`. They also copy symbol-keyed properties and invoke getters, which the clone algorithm treats differently.
- Does structuredClone preserve the prototype of a class instance?No. The clone is an ordinary object with `Object.prototype` as its prototype, so `instanceof` fails and prototype methods are missing. Only own enumerable string-keyed data is carried over. If the receiver needs behaviour, it has to rehydrate — pass plain data and reconstruct the instance on the other side.
saying these in an interview costs you the question
- Claims structuredClone is just JSON.parse(JSON.stringify()) under the hood
- Says it copies functions and methods along with data
- Thinks it is a shallow copy like object spread
- Believes class instances arrive with their prototype intact
- Assumes cycles throw the way JSON.stringify throws