Lesson 0003 · ~12 minutes
One skill: decide clone vs transfer, write the transfer list correctly, and predict detachment.
You can say: “large binary the sender is done with → transfer
.buffer; both sides still need the bytes → clone;
BroadcastChannel / IDB have no transfer list.”
Workers lesson 3:
worker.postMessage(u8, [u8.buffer]) moves the
ArrayBuffer; main sees byteLength === 0.
This track owns the same move on
structuredClone()
— same realm, no worker required — and the list rules that bite
everywhere.
const u8 = new Uint8Array(1024);
// Copy bytes — both sides keep a buffer
const copied = structuredClone(u8);
// Move bytes — source detaches
const moved = structuredClone(u8, { transfer: [u8.buffer] });
// u8.byteLength === 0
// moved.byteLength === 1024
MDN: after transfer the original no longer owns the resource. The memory is not duplicated. That is the point.
Two syntaxes, one transfer list:
| Call | List |
|---|---|
structuredClone(value, options) |
{ transfer: [buf] } |
worker.postMessage(value, transfer) |
[buf] (second argument) |
Three rules from MDN:
.buffer is. { transfer: [u8] } throws
DataCloneError.
value and it still detaches — the clone never
sees it.
Large binary; sender is done. Image pipelines, file parse buffers, “freeze the original so nothing else can write it” (MDN’s same-realm example).
Both sides still need the bytes, or the value is ordinary data (Dates, Maps, DTOs). Default for small payloads.
BroadcastChannel, IndexedDB, history.pushState —
structured clone only. You cannot transfer into storage or a BC
post.
If the hard part is “these bytes should exist in only one place after this call”, transfer. If both sides must keep using them, clone. If the API has no transfer list, design for a copy — or don’t put megabytes on that bus.
Transfer moves a resource inside a structured clone. It does not
make HTTP bytes, and it does not revive
Invoice.total(). Same two facts as lessons 1–2.
Each click builds a fresh 16-byte view. Run all five.
Equal-length options. Feedback is immediate.
After
structuredClone(u8, { transfer: [u8.buffer] }),
u8.byteLength is:
structuredClone(u8, { transfer: [u8] }) — the view
itself is in the list.
structuredClone({ n: 1 }, { transfer: [orphan] })
— orphan is an ArrayBuffer not on
the object.
Fan-out a 16MB pixel buffer to other same-origin tabs via BroadcastChannel.
You already transferred u8.buffer. Now
structuredClone(u8).
byteLength === 0 after a buffer transfer.
.buffer, not the typed array. The buffer must
also sit in the value.
ImageBitmap /
OffscreenCanvas — ask.
MDN — Transferable objects.
Thread transfer, structuredClone({ transfer }), and
“the list does not send the resource.” Then the
structuredClone transfer examples.