Lesson 0004 · ~12 minutes
One skill: run a 60-second risk scan — reject the wrong copy tool before it ships, including the orphan transfer list.
In a design review you can kill “clone the Vue store,” “transfer a leftover buffer,” “JSON as a deep clone,” and “clone then postMessage for no reason.”
Scenario C:
structuredClone({ n: 1 }, { transfer: [orphan] }).
MDN
is blunt: the transfer array only says
how certain resources should be sent. It does not send them.
They still detach.
orphan.byteLength === 0 — you just destroyed a buffer.
{ n: 1 } — no buffer arrived.
Same rule on postMessage(value, transfer). If the buffer
is not reachable from value, you get a hole on one side
and a detached corpse on the other. Put the buffer
in the object and in the list.
| Smell | What actually happens | Do instead |
|---|---|---|
| Vue / Pinia / MobX object in the payload |
Proxy is an exotic object →
DataCloneError
(spec)
|
Clone a plain DTO. In Vue 3,
toRaw only unwraps one layer — prefer a data
snapshot you control
|
| Class instance / “smart” model | Flatten — methods and privates gone | DTO now; custom serializer if you need revival |
structuredClone then
postMessage / IDB /
pushState
|
You paid for two clones | Let the implicit API clone, unless you need a snapshot for another reason |
| Full state every frame / huge graph | Serialize blocks the sender (Surma) | Patches, chunking, or transfer — not more clones |
| REST / file “via structuredClone” | Still a JS value, not text |
JSON.stringify (and accept Date/Map loss)
|
| Orphan in the transfer list | Source detaches; clone never sees it | Buffer must be in the value and the list |
Clone throws on functions, DOM, proxies. JSON drops functions and turns Dates into strings. Picking the wrong algorithm hides the bug or explodes a worker. Know which one you are on.
structuredClone(plainData).
.buffer that
is on the payload.
Printable version: integration checklist.
Equal-length options. C comes back as snippet A.
structuredClone({ n: 1 }, { transfer: [orphan] })
— orphan is an ArrayBuffer not on the
object.
A Vue 3 reactive store object is passed to
structuredClone to snapshot a form.
You will worker.postMessage({ type, draft }). The
main thread will keep using draft after the send.
A worker gets the entire 2MB editor document on every keystroke. The UI janks on send.
Persist a domain Invoice (methods, private fields,
schema version) to a file other services read for years.
DataCloneError from a store, an Immer draft, or a
“buffer is detached” you cannot find — ask. Those are this lesson
in production.
Re-read the two paragraphs on MDN — Transferable objects that say transferred resources must be attached to the data object. Then skim the checklist. Next lesson is the decision playbook.