在JavaScript里处理图像、音视频、网络协议或文件读写时,经常会碰到需要直接操作二进制数据的场景。这时候就会涉及两个核心概念:ArrayBuffer和类型化数组(TypedArray)。很多资料把它们混在一起讲,导致初学者以为ArrayBuffer就是某种特殊的数组,实际上两者的职责完全不同。

ArrayBuffer:纯粹的裸内存块
ArrayBuffer代表一段连续的、固定长度的内存区域,它在语义上非常接近C语言里的malloc出来的缓冲区。这段代码本身不包含任何类型信息,也不能直接通过索引访问其中的字节。你只能知道它有多大,以及把它交给别的视图对象去解释。
下面的例子展示了如何创建一个长度为8字节的ArrayBuffer,并尝试直接读取会发生什么:
// 创建8字节的ArrayBuffer const buffer = new ArrayBuffer(8); // 错误用法:ArrayBuffer本身没有索引访问能力 // console.log(buffer[0]); // undefined console.log(buffer.byteLength); // 8
从上面可以看出,ArrayBuffer只关心“有多少字节”,完全不关心“里面放的是什么”。这种设计让JavaScript在底层数据交换时拥有接近原生的内存表达能力,但也要求开发者必须借助视图才能安全地读写内容。
类型化数组:带类型的视图
类型化数组(如Uint8Array、Int16Array、Float32Array)并不是独立的内存,而是建立在ArrayBuffer之上的视图。同一个ArrayBuffer可以被多个不同类型化数组同时观察,它们以各自的字节宽度和符号规则去解释同一片内存。
例如,一个8字节的ArrayBuffer,既可以用Uint8Array看作8个无符号字节,也可以用Float64Array看作1个双精度浮点数。下面代码演示了这种重叠视图的效果:
const buf = new ArrayBuffer(8); // 以8个无符号字节的视角看 const u8 = new Uint8Array(buf); u8[0] = 255; // 以1个双精度浮点数的视角看 const f64 = new Float64Array(buf); console.log(f64[0]); // 一个极小的非整数,因为字节被重新解释
类型化数组的优势在于性能。由于元素类型固定、内存连续,JavaScript引擎可以做大量优化,在大规模数值计算或编解码时比普通数组快很多。缺点是它不够灵活,每个视图的类型和字节序在创建时就固定了,不适合处理混合结构的协议数据。
常见类型化数组对照
不同数据类型对应不同的字节宽度和取值范围,开发时选错类型会导致溢出或精度丢失。下表列出最常用的几种:
| 类型 | 字节数 | 说明 |
|---|---|---|
| Uint8Array | 1 | 无符号8位整数,0到255 |
| Int16Array | 2 | 有符号16位整数,-32768到32767 |
| Float32Array | 4 | 32位浮点数 |
| Float64Array | 8 | 64位双精度浮点数 |
在实际项目中,如果后端传来的二进制包里既有单字节标志位,又有四字节整数,用单一类型化数组会很麻烦,这时就需要更通用的DataView。
DataView:灵活但稍慢的万能视图
DataView也建立在ArrayBuffer之上,但它不限定统一的元素类型,允许你指定每次读写的偏移量、字节序(大端或小端)以及具体类型。它特别适合解析自定义二进制协议。
以下示例用DataView在同一个buffer里先写一个字节,再写一个32位整数:
const buffer = new ArrayBuffer(5); const view = new DataView(buffer); // 在偏移0写一个无符号字节 view.setUint8(0, 10); // 在偏移1写一个32位无符号整数(大端) view.setUint32(1, 1000, false); console.log(view.getUint8(0)); // 10 console.log(view.getUint32(1, false)); // 1000
DataView的代价是每次操作都有方法调用开销,且无法像类型化数组那样直接用数组语法批量遍历。因此,在已知结构单一、追求吞吐量的场景下应优先使用类型化数组;在结构复杂、字段零散的场景下再用DataView补足。
实际应用场景与避坑
在WebSocket接收二进制消息、读取File文件、WebGL传顶点数据等场景中,ArrayBuffer加类型化数组是标准做法。一个常见错误是把字符串直接塞进ArrayBuffer,却忘了JavaScript字符串是UTF-16,需要用TextEncoder转换。
下面展示用TextEncoder把字符串写入ArrayBuffer的正确方式:
const str = '你好'; const encoder = new TextEncoder(); const bytes = encoder.encode(str); const buf = new ArrayBuffer(bytes.length); new Uint8Array(buf).set(bytes); console.log(new Uint8Array(buf)); // 输出UTF-8编码后的字节
另一个坑是字节序。x86架构是小端,而很多网络协议规定大端。用类型化数组时默认跟随宿主平台字节序,跨平台传输数据必须用DataView显式指定,否则在手机或不同设备上解析会完全错乱。
小结
ArrayBuffer是内存容器,类型化数组和DataView是访问容器的不同眼镜。理解三者关系后,就能在JavaScript里安全高效地处理任何二进制任务,不再被乱码或性能问题困扰。
ArrayBufferTypedArrayDataView修改时间:2026-08-01 05:57:25