LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

前端二进制数组完全指南:ArrayBuffer、TypedArray、DataView 一次讲透

freeflydom
2026年7月3日 17:18 本文热度 174

二进制数组是 JavaScript 操作二进制数据的接口,分别有 ArrayBuffer 对象、TypedArray 视图和 DataView 视图,它们都以数组的语法处理二进制数据。

注意,二进制数组并不是真正的数组,而是类似数组的对象。

ArrayBuffer 对象

ArrayBuffer 对象代表内存之中的一段二进制数据,可以通过“视图”进行操作。“视图”部署了数组接口,可以用数组的方法操作内存。

它本身只是一个内存区域,不能直接读写内存数据,需要配合 TypedArray 视图或 DataView 视图使用。

视图

ArrayBuffer 对象作为内存区域,可以存放多种类型的数据。同一段内存,不同数据有不同的解读方式,这就叫做“视图”(view)。ArrayBuffer 有两种视图,一种是 TypedArray 视图,另一种是 DataView 视图。

TypedArray 视图

TypedArray 视图是以固定数据类型(如 Int8、Uint16、Float32 等)读写 ArrayBuffer 二进制数据的数组视图。

在目前标准中共有 12 种类型,其中前端开发最常用的是 Uint8ArrayInt16ArrayFloat32Array

类型每个元素大小说明范围
Int8Array1 字节8 位有符号整数-128 ~ 127
Uint8Array1 字节8 位无符号整数0 ~ 255
Uint8ClampedArray1 字节8 位无符号整数(钳位)0 ~ 255
Int16Array2 字节16 位有符号整数-32768 ~ 32767
Uint16Array2 字节16 位无符号整数0 ~ 65535
Int32Array4 字节32 位有符号整数-2147483648 ~ 2147483647
Uint32Array4 字节32 位无符号整数0 ~ 4294967295
Float16Array2 字节16 位浮点数-65504 ~ 65504
Float32Array4 字节32 位浮点数IEEE 754
Float64Array8 字节64 位浮点数IEEE 754
BigInt64Array8 字节64 位有符号大整数-2^63 ~ 2^63-1
BigUint64Array8 字节64 位无符号大整数0 ~ 2^64-1

Float16ArrayFloat32ArrayFloat64Array 都是有符号的,没有对应的无符号类型。

创建 TypedArray

TypedArray 可以通过指定长度、普通数组、ArrayBuffer 或另一个 TypedArray 创建,其中基于 ArrayBuffer 创建是前端处理二进制数据最常见、最高效的方式。

指定长度创建 TypedArray 语法为:

new TypedArray(length);

例如:

const arr = new Uint8Array(5);
console.log(arr);

输出:

Uint8Array(5)[(0, 0, 0, 0, 0)];

使用普通数组创建 TypedArray 语法为:

new TypedArray(array);

例如:

const arr = new Uint8Array([10, 20, 30]);
console.log(arr);

输出:

Uint8Array(3)[(10, 20, 30)];

基于 ArrayBuffer 创建TypedArray 语法为:

new TypedArray(buffer, byteOffset, length);

例如:

const buffer = new ArrayBuffer(8);
const arr = new Uint8Array(buffer);
console.log(arr.length); // 8
console.log(arr.byteLength); // 8

如果需要从指定位置开始读取:

const buffer = new ArrayBuffer(16);
// 从第4个字节开始,读取4个元素
const arr = new Uint8Array(buffer, 4, 4);

其中:

  • buffer:底层内存

  • 4:起始偏移(单位:字节)

  • 4:元素个数(不是字节数)

这是解析二进制文件(如 WAV、PNG)时最常见的创建方式。


使用另一个 TypedArray 创建 TypedArray 语法为

new TypedArray(typedArray);

例如:

const arr1 = new Uint8Array([1, 2, 3]);
const arr2 = new Uint8Array(arr1);
console.log(arr2);

输出:

Uint8Array(3)[(1, 2, 3)];

注意:

这里会复制数据arr1arr2 不共享内存。


以上 4 种创建 TypedArray 的方式对比:

创建方式示例是否创建新的 ArrayBuffer
指定长度new Uint8Array(8)
普通数组new Uint8Array([1,2,3])
ArrayBuffernew Uint8Array(buffer)否,共享已有内存
TypedArraynew Uint8Array(other)是(复制数据)

TypedArray 常用 API

TypedArray 的 API 和普通 Array 很像,但它是专门为操作二进制数据设计的。

TypedArray 提供了创建、读取、修改、遍历、复制和查找二进制数据的一系列 API。

属性
属性作用
buffer对应的 ArrayBuffer
byteLength当前视图占用的字节数
byteOffset当前视图起始偏移量(字节)
length元素个数
BYTES_PER_ELEMENT每个元素占用的字节数(静态属性)

示例:

const arr = new Uint16Array(10); // 有 10 个元素,所以 length 为 10
console.log(arr.length); // 10
console.log(arr.byteLength); // 20
console.log(arr.byteOffset); // 0
console.log(arr.buffer); // ArrayBuffer
console.log(Uint16Array.BYTES_PER_ELEMENT); // 2

上面代码中, Uint16Array 为 16 位无符号整数,1 个元素占 2 个字节(1 个字节 = 8 位)。

因此 BYTES_PER_ELEMENT 为 2 。

因为有 10 个元素,所以 byteLength 为 10 乘以 2 ,为 20 个字节 。

读写元素

和普通数组一样,可以通过下标访问。

const arr = new Int16Array(3);
arr[0] = 100;
arr[1] = -50;
console.log(arr[0]); // 100
创建和复制
  1. set() 复制另一组数据,也就是将一段内容完全复制到另一段内存。
const arr = new Uint8Array(5);
arr.set([1, 2, 3]);
console.log(arr);

输出:

Uint8Array(5)[(1, 2, 3, 0, 0)];

也可以指定开始位置:

arr.set([8, 9], 3);

结果:

[1, 2, 3, 8, 9];

  1. subarray() 创建共享同一块内存的新视图。
const arr = new Uint8Array([1, 2, 3, 4]);
const sub = arr.subarray(1, 3);
console.log(sub);

输出:

Uint8Array[(2, 3)];

subarray() 不会复制数据。


  1. slice() 复制数据,返回新的 TypedArray
const copy = arr.slice(1, 3);

得到:

Uint8Array[(2, 3)];

subarray() 不同的是:

  • slice():复制数据
  • subarray():共享内存
遍历

for...of 遍历

for (const value of arr) {
  console.log(value);
}

forEach() 遍历

arr.forEach((value, index) => {
  console.log(index, value);
});

entries()

for (const [i, v] of arr.entries()) {
  console.log(i, v);
}

keys()

for (const key of arr.keys()) {
  console.log(key);
}

values()

for (const value of arr.values()) {
  console.log(value);
}
查找

普通数组中的查找方法都可以运用在 TypedArray 中。

arr.includes(10);
arr.indexOf(10);
arr.lastIndexOf(10);
arr.find((v) => v > 100);
arr.findIndex((v) => v > 100);
转换

普通数组中的转换方法对 TypedArray 也适用

arr.join(",");
arr.toString();
Array.from(arr);
[...arr];

例如:

const arr = new Uint8Array([1, 2, 3]);
console.log(Array.from(arr));

输出:

[1, 2, 3];
迭代方法

普通数组中的迭代方法对 TypedArray 也适用

arr.map(...)
arr.filter(...)
arr.reduce(...)
arr.reduceRight(...)
arr.some(...)
arr.every(...)

例如:

const arr = new Uint8Array([1, 2, 3]);
const result = arr.map((x) => x * 2);
console.log(result);

输出:

Uint8Array[(2, 4, 6)];
排序

TypedArray 的排序方法与普通数组的排序方法一样。

arr.sort();
arr.reverse();

例如:

const arr = new Uint8Array([3, 1, 2]);
arr.sort();
console.log(arr);

输出:

Uint8Array[(1, 2, 3)];
填充

普通数组的填充方法对 TypedArray 也适用。

arr.fill(100);

输出:

[100, 100, 100];
判断

普通数组中的判断方法对 TypedArray 也适用。

arr.includes(10);

返回:

true;
false;
与普通 Array 相比的区别

TypedArray 支持很多与 Array 相同的方法,但也有一些限制:

  • 元素类型固定,例如 Uint8Array 只能存储 0 ~ 255 的整数。
  • 长度固定,创建后不能使用 push()pop()shift()unshift()splice() 等会改变长度的方法。

例如:

const arr = new Uint8Array([1, 2, 3]);
arr.push(4); // ❌ TypeError

这是因为 TypedArray 的底层 ArrayBuffer 大小是固定的,不能像普通数组那样动态扩容。

复合视图

在介绍 DataView 视图前,先介绍复合视图。

复合视图就是多个不同的视图(TypedArrayDataView)同时查看、操作同一个 ArrayBuffer

先看普通视图

假设有一块 8 字节内存:

const buffer = new ArrayBuffer(8);
const view = new Uint8Array(buffer);

此时 buffer 内存中,只有 1 个 Uint8Array 视图。

再看复合视图

同一块内存:

const buffer = new ArrayBuffer(8);
const uint8 = new Uint8Array(buffer);
const uint16 = new Uint16Array(buffer);
const dataView = new DataView(buffer);

此时 buffer 内存中,有 3 个视图,分别为 Uint8ArrayUint16ArrayDataView。这就是复合视图。

多个视图共享同一块内存。

最直观的例子

const buffer = new ArrayBuffer(4);
const uint8 = new Uint8Array(buffer);
const uint16 = new Uint16Array(buffer);
uint8[0] = 1;
uint8[1] = 0;
console.log(uint16[0]);

输出:

1;

因为 uint8uint16 共享同一块内存,所以 uint8 中的修改,也会影响 uint16 中的数据。

类比:同一本书

ArrayBuffer 想象成一本书。

按字阅读:

今
天
天
气
不
错

相当于:

Uint8Array;

按词阅读:

今天
天气
不错

相当于:

Uint16Array;

任意位置阅读:

从第3个字开始读

相当于:

DataView;

这三个人读的是:

同一本书

只是阅读方式不同。

这就是复合视图的本质。

为什么需要复合视图?

解析二进制文件时非常常见。

例如 WAV 文件由文件头、元数据和 PCM 数据组成。

function readString(view, offset, length) {
  let str = "";
  for (let i = 0; i < length; i++) {
    str += String.fromCharCode(view.getUint8(offset + i));
  }
  return str;
}
async function main() {
  const response = await fetch("/5s_16k_mono_16bit_440hz.wav");
  const buffer = await response.arrayBuffer();
  const view = new DataView(buffer);
  const chunkID = readString(view, 0, 4); // "RIFF"
  const pcm = new Int16Array(buffer, 44); // PCM 数据
}
main();

在上面代码中,使用 DataView 读取文件头,使用 Int16Array 读取 PCM 数据,它们共享同一个 ArrayBuffer。

这是复合视图在实际开发中的典型用法。

DataView 视图

DataView 是一种可以按照任意数据类型和任意字节偏移量灵活读写 ArrayBuffer 的视图。

const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setInt16(0, 1000);
console.log(view.getInt16(0)); // 1000

TypedArray 的区别为:

TypedArray  = 整块内存按同一种类型查看
DataView    = 内存中的任意位置按任意类型查看

可以说 DataView 是一个"万能二进制读取器",可以从 ArrayBuffer 的任意位置,以任意数据类型读取或写入数据,非常适合解析 WAV、PNG、网络协议等二进制格式。

创建 DataView

DataView 必须基于 ArrayBuffer 创建,语法为:

new DataView(buffer, byteOffset?, byteLength?)
  • buffer:要操作的 ArrayBuffer
  • byteOffset:起始偏移(字节),默认 0
  • byteLength:视图长度(字节),默认到 buffer 末尾
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
const view2 = new DataView(buffer, 2, 4); // 从第2字节开始,读4字节

DataView 常用 API

DataView 的 API 很有规律:getXxx() 用于读取数据,setXxx() 用于写入数据,Xxx 表示数据类型(如 Uint16Float32),再配合 bufferbyteOffsetbyteLength 三个属性,就可以灵活地操作任意二进制数据。

属性
属性作用
buffer对应的 ArrayBuffer
byteLength视图占用的字节数
byteOffset视图在 buffer 中的起始偏移
读取方法

从指定偏移量以指定类型读取数据,语法为 view.get<Type>(byteOffset, littleEndian?)

方法作用字节数
getInt8()读取 8 位有符号整数1
getUint8()读取 8 位无符号整数1
getInt16()读取 16 位有符号整数2
getUint16()读取 16 位无符号整数2
getInt32()读取 32 位有符号整数4
getUint32()读取 32 位无符号整数4
getFloat16()读取 16 位浮点数2
getFloat32()读取 32 位浮点数4
getFloat64()读取 64 位浮点数8
getBigInt64()读取 64 位整数(BigInt)8
getBigUint64()读取 64 位无符号整数(BigInt)8

littleEndian 默认为 false(大端序),设 true 为小端序。

写入方法

在指定偏移量以指定类型写入数据,语法为 view.set<Type>(byteOffset, value, littleEndian?)

方法作用字节数
setInt8()写入 8 位有符号整数1
setUint8()写入 8 位无符号整数1
setInt16()写入 16 位有符号整数2
setUint16()写入 16 位无符号整数2
setInt32()写入 32 位有符号整数4
setUint32()写入 32 位无符号整数4
setFloat16()写入 16 位浮点数2
setFloat32()写入 32 位浮点数4
setFloat64()写入 64 位浮点数8
setBigInt64()写入 64 位整数(BigInt)8
setBigUint64()写入 64 位无符号整数(BigInt)8

示例:

const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
// 写入
view.setUint8(0, 82); // 第0字节写入 'R' (ASCII 82)
view.setUint16(2, 44100, true); // 第2字节写入 44100(小端序)
// 读取
console.log(view.getUint8(0)); // 82
console.log(view.getUint16(2, true)); // 44100

关于 littleEndian

除了 getInt8()getUint8()setInt8()setUint8()(它们只有 1 个字节,不涉及字节顺序),其他多字节方法都有一个可选参数:

view.getUint32(offset, true);

其中:

true

表示:

按小端字节序(Little Endian)读取或写入。

字节序(Endianness)指多字节数据在内存中的存放顺序。以占据 4 个字节的 16 进制数 0x12345678 为例:

大端序(Big Endian):高位字节在前
地址:  [0]   [1]   [2]   [3]
数据:  0x12  0x34  0x56  0x78  → 符合人类阅读习惯
小端序(Little Endian):低位字节在前
地址:  [0]   [1]   [2]   [3]
数据:  0x78  0x56  0x34  0x12  → CPU 运算更高效
  • 大端序:高字节存低地址,像"从左到右"读数字,网络协议常用
  • 小端序:低字节存低地址,x86/ARM 等主流 CPU 默认的字节序。

下面的函数可以用来判断,当前视图是小端字节序,还是大端字节序。

const BIG_ENDIAN = Symbol("BIG_ENDIAN");
const LITTLE_ENDIAN = Symbol("LITTLE_ENDIAN");
function getPlatformEndianness() {
  let arr32 = Uint32Array.of(0x12345678);
  let arr8 = new Uint8Array(arr32.buffer);
  switch (arr8[0] * 0x1000000 + arr8[1] * 0x10000 + arr8[2] * 0x100 + arr8[3]) {
    case 0x12345678:
      return BIG_ENDIAN;
    case 0x78563412:
      return LITTLE_ENDIAN;
    default:
      throw new Error("Unknown endianness");
  }
}

总结

  1. ArrayBuffer — 原始二进制内存,不能直接读写,需通过视图操作。
  2. TypedArray — 固定类型的数组视图,4 种创建方式(基于 ArrayBuffer 最高效),支持丰富的数组 API,但长度和类型均固定。
  3. 复合视图 — 多个视图共享同一块 ArrayBuffer,是解析二进制文件的核心技巧。类比"同一本书,按字、按词、任意位置三种读法"。
  4. DataView — 万能二进制读取器,可在任意偏移量以任意类型读写,getXxx()/setXxx() 方法覆盖所有数据类型。
  5. 字节序 — 大端序(高位在前,网络协议常用)vs 小端序(低位在前,主流 CPU 默认小端序)。DataView 的多字节方法通过 littleEndian 参数控制;用 Uint32Array + Uint8Array 的复合视图可探测平台字节序。
  6. getPlatformEndianness() — 将 4 字节按大端序权重(×256³、×256²、×256、×1)重组,比对结果判断平台字节序。

核心思想:一块内存,多种解读 — 这正是二进制数组的精髓。

​转自https://juejin.cn/post/7656726047949914121


该文章在 2026/7/3 17:18:13 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-9  粤公网安备44030602007207号