Lesson 0002 · ~12 minutes
One skill: look at a value and predict keep / flatten / throw — so you stop treating a clone as a document or a living class.
You can say: “that Date stays a Date; that
method throws; that Invoice becomes
{ n } with Object.prototype — and a REST
body still needs JSON.stringify.”
Scenario B and D were the same mix-up.
structuredClone()
returns another
JavaScript value. It is not HTTP bytes, and it is not
your class with methods. A REST API wants a
JSON document.
An Invoice that other services must read for years wants a
custom serializer
(version + revival). The type matrix is how you see that.
MDN lists supported types. The HTML walk decides what happens. Compress it to three buckets:
Same kind, new identity.
Date, Map, Set, arrays,
buffers, Blob/File, cycles,
Error, undefined keys, most primitives
(not symbol).
New ordinary object. Enumerable own data only. Prototype, methods, private fields gone. Getters run once; the result is stored as a data property.
DataCloneError: functions, DOM nodes, symbol
values, Promise, WeakMap,
Proxy. Loud, unlike JSON’s silent drop.
Class instances land in
flatten, not throw. That is why “clone the Invoice and write the
file” is the wrong durability plan — you get
{ n: 3 }, not total() or
#id.
For an ordinary object the spec (step 26.4) does this:
clone.self === clone.
[[Get]]. A getter
runs; the clone stores the returned number/string as a
normal data field (no getter on the copy).
Object. Your class prototype is
not restored
(MDN: prototype chain is not walked).
Built-ins with special slots (Date, Map,
ArrayBuffer, …) take earlier branches and
keep their kind.
Callables, platform objects that are not
[Serializable], Promises, WeakMaps, and Proxies
throw.
Send data (DTOs). Rebuild behavior on each side. Same rule as worker messages — now you can predict the three outcomes instead of hoping.
Click each preset. Left is structured clone. Right is JSON. Watch the type, the keys, and which side throws.
Things to notice: Date stays a Date on the
left and becomes a string on the right. Map
stays a Map vs {}. A cycle keeps
«self» vs TypeError. A function
throws on the left and is omitted on the right. A class
instance flattens on both sides — clone is not revival.
Equal-length options. Includes a second pass at B and D.
structuredClone(new Date("2020-01-02")) — result?
structuredClone({ n: 1, run() { return 1 } }) —
result?
class Invoice { #id; total() {} } — clone of
new Invoice()?
You already structuredClone(settings). The REST
server wants application/json. Next step?
const o = { n: 1 }; o.self = o; then
structuredClone(o).
DataCloneError).
{}, functions vanish, cycles throw
TypeError.
File, or a getter surprise just
bit you — ask. Those are flatten/throw cases in the wild.
MDN — The structured clone algorithm.
Supported types + “things that don’t work.” For the walk itself
(memory map, enumerable own, [[Get]], Proxy/Promise
throw), skim
WHATWG StructuredSerializeInternal.