arraybuffer 本身无 slice() 方法,零成本分片依赖 typedarray.slice() 创建共享底层内存的新视图;其切片以元素为单位(uint8array 除外),需注意边界与引用管理。

ArrayBuffer.slice() 本身不支持,它不是 ArrayBuffer 的原生方法——直接调用会报错 TypeError: arrayBuffer.slice is not a function。所谓“零成本逻辑分片”,实际依赖的是 TypedArray 的 .slice() 方法,它返回新视图(新 TypedArray 实例),底层仍共享同一段 ArrayBuffer 内存,不复制字节,因此是真正意义上的“零拷贝”逻辑切片。
为什么 ArrayBuffer 没有 slice()?
ArrayBuffer 是纯粹的、不可变的原始内存块容器,设计上不提供任何读写或切片接口。它的职责只是承载数据;访问和操作必须通过 TypedArray(如 Uint8Array)或 DataView 完成。所以“分片”的动作发生在视图层,而非缓冲区层。
用 TypedArray.slice() 实现逻辑分片
TypedArray(如 Uint8Array、Int32Array 等)的 .slice(start, end) 返回一个新视图,其 backing buffer 仍是原 ArrayBuffer,仅改变字节偏移(byteOffset)与长度(byteLength)。只要不触发底层内存复制,就是零成本。
- ✅ 正确:创建子视图,共享内存
const full = new Uint8Array(new ArrayBuffer(10 * 1024 * 1024)); // 10MBconst chunk = full.slice(2 * 1024 * 1024, 3 * 1024 * 1024); // 取第2~3MB,无复制 - ⚠️ 注意:slice 范围以元素为单位,不是字节(除非是 Uint8Array)
例如new Int32Array(buffer).slice(0, 100)取前 100 个 int32(即 400 字节),不是前 100 字节 - ? 小技巧:统一用 Uint8Array 切片最直观,再按需构造其他视图:
const u8 = new Uint8Array(buffer);const subU8 = u8.slice(1000, 2000); // 精确字节范围const asInt32 = new Int32Array(subU8.buffer, subU8.byteOffset, subU8.length / 4);
真正“零成本”的前提条件
不是所有 .slice() 都绝对零拷贝——V8 和 SpiderMonkey 在绝大多数情况下优化为共享视图,但需满足:
- 源 TypedArray 必须由 ArrayBuffer 直接构造(非来自另一个视图的派生,如未被
subarray()或slice()多次链式调用污染) - 切片边界对齐不影响共享(即使不对齐,现代引擎仍共享 buffer,只是 byteOffset 偏移)
- 避免意外触发“detached buffer”或 GC 干预(如原 ArrayBuffer 已被释放,slice 结果将无效)
- 不建议长期持有大量 slice 视图却不释放引用——虽不复制内存,但会阻止原 ArrayBuffer 被 GC 回收
替代方案对比:slice() vs subarray() vs 构造新视图
三者都可实现逻辑分片,语义与行为略有差异:
-
arr.slice(start, end):返回新 TypedArray 实例,独立于原实例(修改 length 不影响原数组),共享 buffer —— 最常用、语义清晰 -
arr.subarray(start, end):也返回新视图、共享 buffer,但返回类型与原数组完全一致(如 Int32Array.subarray → 还是 Int32Array),且某些引擎中更轻量 -
new Uint8Array(buffer, offset, length):手动构造,控制粒度最细,适合跨类型复用同一 buffer 区域
性能上三者在主流引擎中几乎无差别,推荐优先用 .slice(),语义最接近“取一段”。










