可转移对象

可转移对象 是拥有可转移到另一个上下文中的资源的对象,确保这些资源一次只能在一个上下文中使用。转移后,原始对象将不再可用;它不再指向已转移的资源,任何尝试读取或写入该对象的尝试都会抛出异常。

可转移对象 通常用于共享只能安全地公开给单个 JavaScript 线程的资源。例如,一个ArrayBuffer 是一个可转移对象,它拥有一个内存块。当这样的缓冲区在线程之间转移时,关联的内存资源会从原始缓冲区分离,并附加到在新的线程中创建的缓冲区对象。原始线程中的缓冲区对象不再可用,因为它不再拥有内存资源。

转移也可以在使用structuredClone()创建对象深拷贝时使用。在克隆操作之后,转移的资源会被移动而不是复制到克隆对象。

对于 postMessage()structuredClone(),转移的资源必须附加到数据对象,否则它们在接收端将不可用,因为可转移数组只指示如何发送某些资源,但实际上并没有发送它们(尽管它们总是会被分离)。

用于转移对象资源的机制取决于对象。例如,当一个ArrayBuffer 在线程之间转移时,它指向的内存资源会直接在上下文之间以快速高效的零拷贝操作移动。其他对象可以通过复制关联的资源然后将其从旧上下文中删除来转移。

并非所有对象都是可转移的。可转移对象的列表在下面提供

在线程之间传输对象

下面的代码演示了从主线程向web worker 线程发送消息时转移是如何工作的。该Uint8Array 在 worker 中被复制(复制),而其缓冲区被转移。转移后,任何尝试从主线程读取或写入 uInt8Array 的尝试都会抛出异常,但您仍然可以检查 byteLength 以确认它现在为零。

js
// 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

注意:类型化数组一样,Int32ArrayUint8Array可序列化的,但不可转移的。但是,它们的底层缓冲区是ArrayBuffer,它是一个可转移对象。我们可以将 uInt8Array.buffer 发送到数据参数中,但不能将 uInt8Array 发送到传输数组中。

克隆操作期间传输

下面的代码展示了一个structuredClone() 操作,其中底层缓冲区从原始对象复制到克隆对象。

js
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

支持的对象

各种规范中指示可以转移的项目是

浏览器支持应在相应对象的兼容性信息中通过 transferable 子功能表示(参见RTCDataChannel 查看示例)。在撰写本文时,并非所有可转移对象都已更新为包含此信息。

注意: 可转移对象在Web IDL 文件中使用 [Transferable] 属性进行标记。

另请参阅