Transferable objects

Transferable objects are objects that own resources that can be transferred from one context to another, ensuring that the resources are only available in one context at a time. Following a transfer, the original object is no longer usable; it no longer points to the transferred resource, and any attempt to read or write the object will throw an exception.

Transferable objects are commonly used to share resources that can only be safely exposed to a single JavaScript thread at a time. For example, an ArrayBuffer is a transferable object that owns a block of memory. When such a buffer is transferred between threads, the associated memory resource is detached from the original buffer and attached to the buffer object created in the new thread. The buffer object in the original thread is no longer usable because it no longer owns a memory resource.

Transferring may also be used when creating deep copies of objects with structuredClone(). Following the cloning operation, the transferred resources are moved rather than copied to the cloned object.

The mechanism used to transfer an object's resources depends on the object. For example, when an ArrayBuffer is transferred between threads, the memory resource that it points to is literally moved between contexts in a fast and efficient zero-copy operation. Other objects may be transferred by copying the associated resource and then deleting it from the old context.

Not all objects are transferable. A list of transferable objects is provided below.

Transferring objects between threads

The code below demonstrates how transferring works when sending a message from a main thread to a web worker thread. The Uint8Array is copied (duplicated) in the worker while its buffer is transferred. After transfer any attempt to read or write uInt8Array from the main thread will throw, but you can still check the byteLength to confirm it is now zero.

// Create an 8MB "file" and fill it. 8MB = 1024 * 1024 * 8 B
const uInt8Array = new Uint8Array(1024 * 1024 * 8).map((v, i) => i);
console.log(uInt8Array.byteLength); // 8388608

// Transfer the underlying buffer to a worker
worker.postMessage(uInt8Array, [uInt8Array.buffer]);
console.log(uInt8Array.byteLength); // 0

Note: Typed arrays like Int32Array and Uint8Array, are serializable, but not transferable. However their underlying buffer is an ArrayBuffer, which is a transferable object. We could have sent uInt8Array.buffer in the data parameter, but not uInt8Array in the transfer array.

Transferring during a cloning operation

The code below shows a structuredClone() operation where the underlying buffer is copied from the original object to the clone.

const original = new Uint8Array(1024);
const clone = structuredClone(original);
console.log(original.byteLength); // 1024
console.log(clone.byteLength); // 1024

original[0] = 1;
console.log(clone[0]); // 0

// Transferring the Uint8Array would throw an exception as it is not a transferable object
// const transferred = structuredClone(original, {transfer: [original]});

// We can transfer Uint8Array.buffer.
const transferred = structuredClone(original, { transfer: [original.buffer] });
console.log(transferred.byteLength); // 1024
console.log(transferred[0]); // 1

// After transferring Uint8Array.buffer cannot be used.
console.log(original.byteLength); // 0

Supported objects

The items that various specifications indicate can be transferred are:

Browser support should be indicated in the respective object's compatibility information by the transferable subfeature (see RTCDataChannel for an example). At time of writing, not all transferable objects have been updated with this information.

Note: Transferable objects are marked up in Web IDL files with the attribute [Transferable].

See also