ArrayBuffer transfer S4
中文标题:ArrayBuffer 转移
- 阶段: Stage 4
- 状态: 已完成
- ECMAScript 版本: ES2024
- 同步时间: 2026年8月26日
- English original · 官方仓库
该提案向 ArrayBuffer.prototype 添加了 transfer、transferToFixedLength 和 detached getter,使得 ArrayBuffer 的程序化分离和所有权转移成为可能。该 API 支持所有权语义、类似 realloc 的大小调整、将可调整大小的缓冲区固定为固定长度以及权威的分离状态检查等使用场景。
以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。
ArrayBuffer.prototype.transfer 及其相关方法
阶段:4(已包含在 ES2024 中)。此仓库不再活跃。
作者:Shu-yu Guo (@syg)
拥护者:Shu-yu Guo (@syg)、Jordan Harband (@ljharb)、Yagiz Nizipli (@anonrig)
简介
ArrayBuffer 可能被 HTML 的序列化算法转移和分离,但缺少同样表达力的编程式 JS API。编程式 API 对于诸如转移 ArrayBuffer 所有权、优化重新分配(即 realloc 语义)以及将可调整大小的 ArrayBuffer 固定为固定长度等编程模式非常有用。本提案通过向 ArrayBuffer.prototype 添加新方法填补了这一表达力空白。
本提案源自 可调整大小的缓冲区提案。在分离时,可调整大小的缓冲区处于阶段 3,而本提案被降级为阶段 2。
API
动机和使用案例
所有权
“移动并分离原始 ArrayBuffer”方法可用于在使用 ArrayBuffer 时实现所有权语义。这在许多情况下都很有用,例如在写入缓冲区时禁止其他用户修改它。
例如,考虑来自原始转移提案的 @domenic 的以下示例:
根据 await validate(arrayBuffer) 所花费的时间,验证结果可能因传递给 setTimeout 的回调而过时。防御性方法会先复制输入,但这明显性能较差:
使用 transfer,所有权转移可以简洁地表达:
重新分配
相同的 transfer API,在传递 newByteLength 参数时,可以兼作具有与 realloc 相同的表达力。操作系统通常比复制更高效地实现 realloc。
将可调整大小的缓冲区固定为固定长度
transferToFixedLength 方法是 transfer 的一种变体,它总是返回固定长度的 ArrayBuffer。这在缓冲区不再需要可调整大小的情况下很有用,允许实现在可调整大小的缓冲区原地实现时释放虚拟内存(即提前保留地址空间)。
检查分离状态
由于 TypedArray 和 ArrayBuffer 标准化的混乱历史以及保持 Web 兼容性,对于已分离缓冲区上的 TypedArray 视图,某些操作会抛出异常(例如原型方法),而其他操作则返回哨兵值(0 或 undefined)(例如索引访问和长度)。
添加 detached getter 以权威地判断 ArrayBuffer 是否已分离。
目前,没有高效的方法来检测 ArrayBuffer 是否已分离。以下是检测分离状态的一种实现示例,但在 V8 中存在一些缺陷:V8 不会内联带有 try-catch 块的函数。另请参见 此 Node 内部注释。
常见问题解答与设计权衡
为什么同时存在 transfer 和 transferToFixedLength 而不是一个更灵活的方法?
大多数人似乎直觉认为移动语义作为主要使用案例应保留可调整大小性。HTML 序列化中的 ArrayBuffer 转移保留了可调整大小性,与此对称有利于直觉。
灵活的 transfer 也会使 API 设计对于少数使用案例变得复杂,因此采用了独立的 transferToFixedLength 方法。
为什么不能向 transfer 传递新的 maxByteLength?
transfer 的目标之一,除了分离语义,是在效率上可优于用户代码中的复制。作者不清楚对于原地实现的可调整大小缓冲区,在所有流行的操作系统上重新分配虚拟内存页面是否可能且高效。
此外,这增加了复杂性,服务于更少数使用案例。从一开始就应该以足够的最大大小分配可调整大小的缓冲区。
如果性能是目标,为什么不添加新方法而是将写时复制(CoW)实现为透明优化?
一句话:安全。
ArrayBuffer 是攻击 JavaScript 引擎时非常流行的攻击向量。引擎采用的一项重要安全缓解措施是确保 ArrayBuffer 的数据指针是常量且不移动。出于同样原因,可调整大小的缓冲区被指定为允许原地实现。
CoW ArrayBuffer 可以通过移动数据指针实现。当修改 CoW ArrayBuffer 时,会分配新内存并更新后备存储。然而,这与安全缓解措施相冲突。
只有在底层操作系统的额外帮助下,才可能同时实现写时复制 ArrayBuffer 并保持“固定数据指针”安全缓解措施:通过映射标记为 CoW 的新虚拟内存并最初指向与源缓冲区相同的物理页面。然而,这种技术并非可移植。
目前,Google Chrome 认为此缓解措施对安全足够重要,因此不实现 CoW ArrayBuffer。
get detached() 在浏览器和运行时中的使用情况如何?
- Node.js 讨论了为
get detached()添加公共 API - WebKit 在内部
ArrayBuffer类中有isDetached - V8 添加了
v8::ArrayBuffer::WasDetached,后来将其移植到 Node.js 并在 Node webstreams 中使用。
transfer 在浏览器和运行时中的使用情况如何?
大多数浏览器使用 detach() 作为分离 ArrayBuffer 的名称。
- V8 具有
Detach()作为v8::ArrayBuffer::Detach() - WebKit 具有
void detach() - Node.js 有一个内部实现来支持分离内部缓冲区,称为
transferArrayBuffer,并在 Node webstream 中使用。
开放问题
我们真的需要 transferToFixedLength 吗?
完善表达力感觉不错,但承认这里的使用案例不如 transfer 那么引人注目。
历史与致谢
感谢: