ArrayBuffer
ArrayBuffer 对象代表存储二进制数据的一段内存,是一个字节数组。
它不能被直接读写,需要创建视图(TypedArray, DataView)来对它进行操作,视图可以以指定格式操作二进制数据。
TypedArray
TypedArray 并不直接存储数据,它只是一个视图,建立在ArrayBuffer 之上。
是一个父类或者说一个家族。它的成员是像 Int8Array、Uint8Array、Float32Array 等具体的类型化数组构造函数。
Int8Array | 8 位有符号整数 | 1 字节 | -128 到 127 |
Uint8Array | 8 位无符号整数 | 1 字节 | 0 到 255 |
Int16Array | 16 位有符号整数 | 2 字节 | -32768 到 32767 |
Uint16Array | 16 位无符号整数 | 2 字节 | 0 到 65535 |
Int32Array | 32 位有符号整数 | 4 字节 | -2,147,483,648 到 2,147,483,647 |
Uint32Array | 32 位无符号整数 | 4 字节 | 0 到 4,294,967,295 |
Float32Array | 32 位浮点数 (单精度) | 4 字节 | 约 ±3.4e38 |
Float64Array | 64 位浮点数 (双精度) | 8 字节 | 约 ±1.8e308 |
BigInt64Array | 64 位有符号大整数 (ES2020) | 8 字节 | 任意大的整数,需要使用 BigInt 类型 |
BigUint64Array | 64 位无符号大整数 (ES2020) | 8 字节 | 任意大的正整数,需要使用 BigInt 类型 |
Uint8ClampedArray | 8 位无符号整数,溢出时会被钳制 (常用于Canvas) | 1 字节 | 0 到 255,溢出时会自动调整到 0 或 255 |
怎么用?
const buffer = new ArrayBuffer(16); // 创建一个16字节的ArrayBuffer
const uint8 = new Uint8Array(buffer); // 创建一个 Uint8Array 视图
const int32 = new Int32Array(buffer); // 创建一个 Int32Array 视图
console.log(uint8.length); // 16 (16个8位无符号整数)
console.log(int32.length); // 4 (4个32位有符号整数)
//写入
uint8[0] = 255;
//读取
console.log(uint8[0]); // 255
int32[0] = 123456789;
console.log(int32[0]); // 123456789
Uint8ClampedArray是什么:
如果写入的数值小于 0,它会被钳制为 0。 如果写入的数值大于 255,它会被钳制为 255。
这种钳制行为在处理图像像素数据时非常有用。图像的每个颜色通道(红、绿、蓝、透明度)通常用一个 0 到 255 的整数来表示。
当你在进行图像处理(如滤镜、亮度调整、对比度增强等)时,计算结果可能会超出 0-255 的范围。
如果使用普通的 Uint8Array,超出范围的值会环绕,导致颜色出现意想不到的扭曲(比如 256 变成了 0,257 变成了 1,这在颜色上看起来是突变的)。
typedArray的set方法
用于将一个另一个数组中的多个元素一次性复制到当前 Typed Array 的指定 offset 位置。
let array1 = new Float32Array([1, 2, 3]);
let array2 = new Float32Array(5); // A new array with more space
array2.set(array1); // Copies [1, 2, 3] into array2, starting at index 0
console.log(array2); // Output: Float32Array [1, 2, 3, 0, 0]
array2.set([4, 5], 3); // Starts at index 3 and adds [4, 5]
console.log(array2); // Output: Float32Array [1, 2, 3, 4, 5]
DataView
相比 TypedArray,DataView 更加灵活。它允许你在同一 ArrayBuffer 中,以任意字节偏移量和任意字节序(big-endian或little-endian)读取或写入不同类型的数据。
注:大端序(Big-Endian): 将数据的最高有效字节存放在最低的内存地址。小端序(Little-Endian): 将数据的最低有效字节存放在最低的内存地址。计算机内部,小端序被广泛应用于现代 CPU 内部存储数据;而在其他场景,比如网络传输和文件存储则使用大端序。
Dataview的读取方法和写入方法:
//读取方法:这里byteOffset必须写。第二个参数可选,为布尔值。true: 小端序。false (或省略): 大端序。
getInt8(byteOffset): 读取一个有符号8位整数。
getUint8(byteOffset): 读取一个无符号8位整数。
getInt16(byteOffset, littleEndian): 读取一个有符号16位整数。
getUint16(byteOffset, littleEndian): 读取一个无符号16位整数。
getInt32(byteOffset, littleEndian): 读取一个有符号32位整数。
getUint32(byteOffset, littleEndian): 读取一个无符号32位整数。
getFloat32(byteOffset, littleEndian): 读取一个32位浮点数。
getFloat64(byteOffset, littleEndian): 读取一个64位浮点数。
//写入方法:byteOffset是开始写入字节的起始偏移量。第三个参数可选,同上
setInt8(byteOffset, value): 写入一个有符号8位整数。
setUint8(byteOffset, value): 写入一个无符号8位整数。
setInt16(byteOffset, value, littleEndian): 写入一个有符号16位整数。
setUint16(byteOffset, value, littleEndian): 写入一个无符号16位整数。
setInt32(byteOffset, value, littleEndian): 写入一个有符号32位整数。
setUint32(byteOffset, value, littleEndian): 写入一个无符号32位整数。
setFloat32(byteOffset, value, littleEndian): 写入一个32位浮点数。
setFloat64(byteOffset, value, littleEndian): 写入一个64位浮点数。
示例;
// 1. 创建一个 ArrayBuffer
const buffer = new ArrayBuffer(16); // 创建一个16字节的缓冲区
//ArrayBuffer: [ B0 | B1 | B2 | B3 | B4 | B5 | B6 | B7 | B8 | B9 | B10 | B11 | B12 | B13 | B14 | B15 ]
//Byte Index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 2. 创建一个 DataView 来操作这个 ArrayBuffer
const dataView = new DataView(buffer);
// 3. 写入数据
// 在偏移量 0 处写入一个 8 位无符号整数 (0-255)
dataView.setUint8(0, 255); // 0xFF
// 在偏移量 1 处(索引为1)写入一个 16 位有符号整数
// 默认是大端序 (false 或不传)
dataView.setInt16(1, -12345); // -12345 (0xCFC7)
// 在偏移量 3 处写入一个 32 位浮点数 (使用小端序)
dataView.setFloat32(3, 3.14159, true); // true 表示小端序
// 在偏移量 7 处写入一个 64 位浮点数 (使用大端序)
dataView.setFloat64(7, 123456789.1234567, false); // false 表示大端序 (或不传)
console.log("--- 写入数据完成 ---");
// 4. 读取数据
console.log("Uint8 at offset 0:", dataView.getUint8(0)); // 255
// 读取在偏移量 1 处的 16 位有符号整数 (默认大端序)
console.log("Int16 at offset 1 (Big-Endian):", dataView.getInt16(1)); // -12345
// 读取在偏移量 3 处的 32 位浮点数 (小端序)
console.log("Float32 at offset 3 (Little-Endian):", dataView.getFloat32(3, true)); // 3.141590118408203
// 读取在偏移量 7 处的 64 位浮点数 (大端序)
console.log("Float64 at offset 7 (Big-Endian):", dataView.getFloat64(7, false)); // 123456789.1234567
两者区别
| 特性 | Typed Arrays | DataView |
|---|---|---|
| 用途 | 处理同构的二进制数据块 | 处理异构的二进制数据,需要精细控制字节顺序和偏移量 |
| 数据类型 | 每个视图固定一种数据类型 | 可以在同一个视图中读写不同类型的数据 |
| 访问方式 | 数组索引 ([]),方便迭代和批量操作 | 方法调用 (get*(), set*()) |
| 字节顺序 | 平台默认字节顺序(无法直接控制) | 可以明确指定大端序或小端序 |
| 性能 | 通常更优,因为类型已知,可高度优化 | 略低于 Typed Arrays,因为每次访问都需要类型检查和偏移量计算 |
| 解释数据 | 同构 (Homogeneous) :所有元素都视为同一种类型。例如,Uint8Array 认为所有字节都是无符号 8 位整数。 | 异构 (Heterogeneous) :可以读取/写入不同类型的数据到 ArrayBuffer 的任何指定字节偏移量。 |
| 元素类型 | 固定:一旦创建,其元素类型就确定了(如 Int32Array 只能存取 32 位整数)。 | 灵活:通过不同的方法(getInt8, getFloat32 等)在运行时指定要读写的类型。 |
| 访问方式 | 数组索引:通过 array[index] 的方式访问元素,类似常规数组。每个 index 代表一个类型化数据单元。 | 偏移量:通过 dataView.get*(byteOffset, littleEndian) 或 dataView.set*(byteOffset, value, littleEndian) 的方式,精确到字节偏移量。 |
| 读写粒度 | 数据类型单位:每次读写操作都是以其类型的大小为单位(如 Int32Array 读写 4 字节)。 | 字节单位:每次读写操作都从指定的字节偏移量开始,并根据方法名(如 getFloat64)读取相应的字节数。 |
| 创建方式 | new TypeArray(buffer) | new DataView(buffer, [byteOffset], [byteLength]) |
| 适用场景 | - 高效处理大量相同类型数据- 对性能要求较高(typed array比dataview性能高一些)。 | - 从一个二进制缓冲区中读取或写入不同类型的数据时 |