JavaScript 可调整缓冲区:缩短后视图变成零长,再扩大为什么旧字节没有回来
用可调整ArrayBuffer接收数据时,底层容量一缩短,原来四项的Uint8Array突然报告长度零。随后把容量扩大,它又恢复四项,但原来尾部数据已经变成零。这些现象属于缓冲区大小与视图有效范围变化,不能用普通数组截取的直觉理解。
下面保存为 demo.mjs,运行 node demo.mjs。需要支持ArrayBuffer.resize的运行时,程序先检查该方法存在;仅分配最多八字节内存,不读取文件或网络。两种视图都从字节偏移二开始,差别只在是否显式传入长度四。
AI模型生成概念示意:六格缓冲区缩成三格后再扩大,新增区域为空,原尾部内容不会复原;图中的格子不代表内存实测截图。
import assert from 'node:assert/strict';
assert.equal(typeof ArrayBuffer.prototype.resize, 'function');
const buffer = new ArrayBuffer(6, { maxByteLength: 8 });
const whole = new Uint8Array(buffer);
whole.set([10, 20, 30, 40, 50, 60]);
const fixed = new Uint8Array(buffer, 2, 4);
const tracking = new Uint8Array(buffer, 2);
console.log('initial lengths: ' + fixed.length + ' ' + tracking.length);
assert.deepEqual([...fixed], [30, 40, 50, 60]);
buffer.resize(3);
assert.equal(fixed.length, 0);
assert.equal(fixed[0], undefined);
assert.throws(() => fixed.slice(), TypeError);
assert.deepEqual([...tracking], [30]);
console.log('after shrink: fixed=' + fixed.length + ', tracking=' + tracking.length);
console.log('fixed index: ' + fixed[0] + ', slice: TypeError');
buffer.resize(6);
assert.equal(fixed.length, 4);
assert.equal(tracking.length, 4);
assert.deepEqual([...whole], [10, 20, 30, 0, 0, 0]);
assert.deepEqual([...fixed], [30, 0, 0, 0]);
console.log('after regrow: ' + [...whole].join(','));
console.log('fixed restored: ' + [...fixed].join(','));
assert.throws(() => buffer.resize(9), RangeError);
assert.equal(buffer.byteLength, 6);
console.log('above maximum: RangeError, length=' + buffer.byteLength);固定长度视图越界后,不会只留下可见前半段
initial lengths: 4 4 说明两种视图起初覆盖相同的四个字节。缩到三字节后,固定视图要求从偏移二起连续容纳四字节,整个要求已无法满足,因此长度报告为零;长度跟踪视图则按当前剩余范围计算,仍有一个元素三十。
fixed index: undefined, slice: TypeError 展示两种访问入口。直接读下标返回undefined,但调用本例中的slice会因视图越界抛出TypeError。不能只看某次下标访问没有抛错,就认定随后所有TypedArray方法都可以正常运行。
视图可以恢复,丢掉的数据不会自动恢复
扩大回六字节以后,完整缓冲区为10,20,30,0,0,0,固定视图恢复为30,0,0,0。它的原始偏移和长度要求重新能够满足,所以四项范围再次有效。这里没有新建fixed对象,恢复的是同一个视图对象的可用状态。
缩容时被截掉的40、50、60已经不再属于缓冲区内容。重新长出来的字节为零,不能把resize当作临时隐藏后缀的可逆操作。若尾部还需要保留,应在缩容之前明确复制到独立存储,并为复制成功与新范围做好检查。
容量上限、当前长度与视图长度分别验收
构造时maxByteLength设为八,当前长度先为六,之后可以在允许范围内改变。最后尝试扩大到九抛出RangeError,断言也确认失败后仍为六。最大容量是创建时约定的边界,不是当前已经可读写的字节数。
本例使用Uint8Array,每个元素正好一字节,所以长度变化易于核算。换成其他TypedArray时,还要考虑元素宽度与对齐要求。若跟踪视图的起点本身也落到新的缓冲区末尾之外,它同样会越界,并不是任何缩容后都能继续读取。
把缓冲区交给可能调用resize的组件后,旧视图的有效性就依赖共享的容量状态。读取前后可能发生重入或异步交接时,应设计清楚谁有权调整大小,并在关键操作处重新验证范围;不要长期缓存一次读到的length,假定它永远有效。
资料核对日期:2026年10月2日。完整代码在 Node.js v24.19.0 实跑,缩容、扩大、类型错误、容量错误及字节内容断言全部通过。


