JavaScript JavaScript 类型化数组
原创JavaScript 类型化数组
JavaScript 类型化数组是一种特殊的数据结构,它允许开发者直接操作内存中的原始二进制数据。这种数据结构类似于数组,但提供了更高效的二进制数据处理能力,特别适用于处理大量数据或与底层系统交互的场景。
类型化数组并非为了取代 JavaScript 中的普通数组,而是为开发者提供了一种操作二进制数据的接口。在处理音频视频编辑、访问 WebSocket 原始数据等与平台相关的特性时,类型化数组显得尤为有用。类型化数组中的每个元素都是以特定格式表示的原始二进制值,支持从 8 位整数到 64 位浮点数的多种二进制格式。
类型化数组拥有许多与普通数组相似的方法和语义,但它们并不完全相同。在类型化数组上调用 Array.isArray() 会返回 false,而且并非所有普通数组的方法都适用于类型化数组,如 push 和 pop 方法。
为了实现最大的灵活性和效率,JavaScript 将类型化数组的实现分为两个核心部分:缓冲和视图。缓冲是一种表示数据块的对象,它本身没有格式,也不提供访问其内容的机制。要访问缓冲中的内存,需要使用视图。视图提供了上下文,包括数据类型、起始偏移量和元素数量。
缓冲
JavaScript 中主要有两种类型的缓冲:ArrayBuffer 和 SharedArrayBuffer。它们都是内存块的低级表示,虽然名称中包含"array",但与普通数组关系不大,因为不能直接读写它们。缓冲是通用的对象,只包含原始数据,需要通过视图来访问其内存。
缓冲支持以下操作:
- 分配:创建新缓冲时,会分配新的内存块并初始化为 0。
- 复制:使用 slice() 方法可以高效复制缓冲的一部分数据,无需手动复制每个字节。
- 转移:使用 transfer() 和 transferToFixedLength() 方法可以将内存块所有权转移给新缓冲对象,适用于在不同执行上下文间转移数据而不复制的情况。转移后,原始缓冲将不再可用。SharedArrayBuffer 不能被转移。
- 调整大小:使用 resize() 方法可以调整内存块大小,只要不超过预设的 maxByteLength 限制。SharedArrayBuffer 只能增长,不能缩小。
ArrayBuffer 和 SharedArrayBuffer 的主要区别在于所有权。ArrayBuffer 在同一时刻只能属于单个执行上下文,传递给其他上下文时会转移所有权,确保只有一个上下文能访问内存。而 SharedArrayBuffer 传递给其他上下文时不会被转移,允许多个上下文同时访问,这时可能需要使用 Atomics 方法处理竞争条件。
视图
视图是访问缓冲数据的接口,主要有两种类型:类型化数组视图和 DataView。类型化数组提供了实用方法,便于转换二进制数据;DataView 则更底层,允许精确控制数据访问方式。这两种视图的读写数据方式有很大不同。
两种视图都会使 ArrayBuffer.isView() 返回 true,并共享以下属性:
- buffer
- 视图所引用的底层缓冲。
- byteOffset
- 视图相对于缓冲起始位置的偏移量(以字节为单位)。
- byteLength
- 视图的长度(以字节为单位)。
两种视图的构造函数都接受上述三个参数,但类型化数组构造函数还接受 length 参数作为元素数量而非字节长度。
类型化数组视图
类型化数组视图有自描述的名称,提供了各种常见数值类型的视图,如 Int8、Uint32 和 Float64 等。其中 Uint8ClampedArray 是一种特殊类型,它会将值钳制到 0-255 之间,在 Canvas 数据处理等场景中特别有用。
以下是主要的类型化数组类型及其特性:
| 类型 | 值范围 | 字节数 | 描述 |
|---|---|---|---|
| Int8Array | -128~127 | 1 | 8 位有符号整数(补码) |
| Uint8Array | 0~255 | 1 | 8 位无符号整数 |
| Uint8ClampedArray | 0~255 | 1 | 8 位无符号整数(值会被裁剪) |
| Int16Array | -32768~32767 | 2 | 16 位有符号整数(补码) |
| Uint16Array | 0~65535 | 2 | 16 位无符号整数 |
| Int32Array | -2147483648~2147483647 | 4 | 32 位有符号整数(补码) |
| Uint32Array | 0~4294967295 | 4 | 32 位无符号整数 |
| Float32Array | -3.4E38~3.4E38 | 4 | 32 位 IEEE 浮点数 |
| Float64Array | -1.8E308~1.8E308 | 8 | 64 位 IEEE 浮点数 |
| BigInt64Array | -2^63~2^63-1 | 8 | 64 位有符号整数(补码) |
| BigUint64Array | 0~2^64-1 | 8 | 64 位无符号整数 |
所有类型化数组类型都由 TypedArray 类定义相同的方法和属性,唯一区别在于底层数据类型和字节数。类型化数组原则上固定长度,不支持改变长度的方法如 pop、push、shift 等。但它们提供了额外的 set 和 subarray 方法,用于优化"同缓冲多视图"场景。
DataView
DataView 是一种底层接口,提供操作缓冲中任意数据的 getter/setter API。这对于操作不同类型数据的场景特别有用,因为它可以控制字节序。默认使用大端字节序,但可以改为小端字节序。DataView 不需要对齐,多字节读写可以从任意偏移量开始。
使用类型化数组的 Web API
许多 Web API 使用类型化数组来处理二进制数据:
- FileReader.prototype.readAsArrayBuffer():读取 Blob 或 File 的内容
- XMLHttpRequest.prototype.send():支持使用类型化数组和 ArrayBuffer 作为参数
- ImageData.data:是一个 Uint8ClampedArray,描述按 RGBA 序列排列的颜色数据
示例
使用视图和缓冲
创建一个 16 字节固定长度的缓冲:
const buffer = new ArrayBuffer(16);
创建一个视图,将缓冲内的数据视为 32 位有符号整数数组:
const int32View = new Int32Array(buffer);
像普通数组一样访问元素:
for (let i = 0; i < int32View.length; i++) {
int32View[i] = i * 2;
}
相同数据上的多个视图
可以在同一数据上创建多个视图:
const int16View = new Int16Array(buffer);
for (let i = 0; i < int16View.length; i++) {
console.log(`索引 ${i}:${int16View[i]}`);
}
修改一个视图会影响共享同一缓冲的其他视图:
int16View[0] = 32;
console.log(`32 位数组的索引 0 处数据是:${int32View[0]}`);
从缓冲读取文本
使用 TextDecoder API 读取 UTF-8 文本:
const buffer = new ArrayBuffer(8);
const uint8 = new Uint8Array(buffer);
uint8.set([228, 189, 160, 229, 165, 189]);
const text = new TextDecoder().decode(uint8);
console.log(text); // "你好"
使用 String.fromCharCode() 读取 UTF-16 文本:
const buffer = new ArrayBuffer(8);
const uint16 = new Uint16Array(buffer);
uint16.set([0x4f60, 0x597d]);
const text = String.fromCharCode(...uint16);
console.log(text); // "你好"
使用复杂的数据结构
通过组合缓冲和不同类型视图,可以操作复杂数据结构。例如,处理类似 C 语言结构体的数据:
struct someStruct {
unsigned long id;
char username[16];
float amountDue;
};
在 JavaScript 中可以这样访问:
const buffer = new ArrayBuffer(24);
const idView = new Uint32Array(buffer, 0, 1);
const usernameView = new Uint8Array(buffer, 4, 16);
const amountDueView = new Float32Array(buffer, 20, 1);
转换为普通数组
有时需要将类型化数组转换为普通数组,可以使用 Array.from() 或展开语法:
const typedArray = new Uint8Array([1, 2, 3, 4]);
const normalArray = Array.from(typedArray);
// 或
const normalArray = [...typedArray];
参见
- 使用类型化数组实现快速的 Canvas 像素操作
- 类型化数组:浏览器中的二进制数据
- 字节序
- ArrayBuffer
- DataView
- TypedArray
- SharedArrayBuffer
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权本站发表,未经许可,不得转载。
开发学习网




