Skip to content

JavaScript 中的二进制表示和处理机制与底层语言(如 C/C++)不同,因为 JS 是一门高级动态语言,其设计初衷并非直接操作内存。但随着 Web 应用的发展,尤其是音视频处理、WebGL、网络通信等需求的出现,JS 也逐步引入了对原始二进制数据的支持。

下面我们彻底讲透 JavaScript 中的二进制表示和操作体系,涵盖从基本概念到实际应用。


一、JavaScript 中的“二进制”到底是什么?

在 JavaScript 中,“二进制”指的是:

以字节为单位的原始数据块,通常用于处理文件、网络流、图像、音频、视频、加密等底层资源。

这种数据在 JS 中以 ArrayBuffer 及其相关类型(如 TypedArrayDataView)的形式存在。


二、核心组成部分:三大支柱

JavaScript 处理二进制的核心由三个部分组成:

1. ArrayBuffer

  • 代表一块固定长度的原始二进制缓冲区(类似 C 中的 char* buffer)。

  • 本身不可读写,只是一个容器。

  • 创建后大小不可变。

js
const buffer = new ArrayBuffer(16); // 分配 16 字节
console.log(buffer.byteLength); // 16

2. 类型化数组(TypedArray)

ArrayBuffer 的“视图”,用于解释缓冲区中的内容。它决定了如何读写二进制数据。

常见的类型化数组:

类型单个元素大小(字节)说明
Int8Array18位有符号整数
Uint8Array18位无符号整数
Uint8ClampedArray1常用于图像处理(超出范围时限制在 0-255)
Int16Array216位有符号整数
Uint16Array216位无符号整数
Int32Array432位有符号整数
Uint32Array432位无符号整数
Float32Array432位浮点数
Float64Array864位浮点数

✅ 所有类型化数组都继承自 TypedArray(注意没有叫 TypedArray 的构造函数,只是一个统称)。

示例:用不同视图读写同一块内存

js
const buffer = new ArrayBuffer(8); // 8 字节缓冲区

const int32View = new Int32Array(buffer);
int32View[0] = 42;
int32View[1] = 99;

const int8View = new Int8Array(buffer);
console.log(int8View); 
// 输出:[42, 0, 0, 0, 99, 0, 0, 0]
// 因为 42 存储为小端模式:低位字节在前

🔍 注意:多字节数据的存储顺序涉及字节序(Endianness),见下文。


3. DataView

  • 提供更底层、更灵活的读写方式。

  • 允许指定字节偏移和字节序(大端/小端)。

  • 适合处理混合类型或跨平台兼容的数据。

js
const buffer = new ArrayBuffer(8);
const dv = new DataView(buffer);

dv.setInt32(0, 42, true);     // 偏移 0,写入 42,小端
dv.setFloat32(4, 3.14, false); // 偏移 4,写入 3.14,大端

console.log(dv.getInt32(0, true));    // 42
console.log(dv.getFloat32(4, false)); // 3.14

三、内存布局与字节序(Endianness)

什么是字节序?

多字节数据(如 int32float64)在内存中会占用多个字节,但这些字节如何排列有两种方式:

类型说明
小端序(Little-endian)低位字节在前(x86/AMD 架构常见)
大端序(Big-endian)高位字节在前(网络传输标准)

示例:数字 0x12345678 存储为 4 字节

内存地址 →0123
Little-endian0x780x560x340x12
Big-endian0x120x340x560x78

DataView 允许你指定读写时的字节序,而 TypedArray 通常使用平台默认字节序(大多数是小端)。


四、二进制数据来源与转换

1. 文件 → ArrayBuffer(浏览器环境)

js
const fileInput = document.querySelector('input[type="file"]');

fileInput.addEventListener('change', async (e) => {
  const file = e.target.files[0];
  const arrayBuffer = await file.arrayBuffer(); // File API 提供
});

2. ArrayBuffer → Base64 字符串

js
function bufferToBase64(buffer) {
  const bytes = new Uint8Array(buffer);
  let binary = '';
  for (let i = 0; i < bytes.byteLength; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return btoa(binary); // Base64 编码
}

3. Base64 → ArrayBuffer

js
function base64ToBuffer(base64) {
  const binary = atob(base64);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes.buffer;
}

4. 字符串 → ArrayBuffer(UTF-8 编码)

js
function stringToBuffer(str) {
  const encoder = new TextEncoder(); // UTF-8 编码器
  return encoder.encode(str).buffer;
}

const buffer = stringToBuffer("Hello 世界");

5. ArrayBuffer → 字符串

js
function bufferToString(buffer) {
  const decoder = new TextDecoder('utf-8');
  return decoder.decode(buffer);
}

console.log(bufferToString(buffer)); // "Hello 世界"

五、实际应用场景

1. 图像处理(Canvas)

js
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, width, height);
const pixels = new Uint8ClampedArray(imageData.data); // RGBA 每个分量 1 字节
// 修改像素
for (let i = 0; i < pixels.length; i += 4) {
  pixels[i]     = 255; // R
  pixels[i + 1] = 0;   // G
  pixels[i + 2] = 0;   // B
  // A 不变
}
ctx.putImageData(imageData, 0, 0);

2. WebSocket 二进制通信

js
const socket = new WebSocket('ws://example.com');
socket.binaryType = 'arraybuffer';

socket.onmessage = function(event) {
  const buffer = event.data; // ArrayBuffer
  const view = new Uint8Array(buffer);
  console.log('Received binary data:', view);
};

// 发送
socket.send(new Uint8Array([1, 2, 3, 4]).buffer);

3. WebAssembly 内存交互

WebAssembly 的内存就是一块 ArrayBuffer,JS 可以直接读写:

js
const wasmMemory = new WebAssembly.Memory({ initial: 256 });
const buffer = wasmMemory.buffer; // JS 可访问的 ArrayBuffer
const uint8View = new Uint8Array(buffer);

4. 加密操作(Crypto API)

js
const data = new TextEncoder().encode('hello');
crypto.subtle.digest('SHA-256', data).then(hashBuffer => {
  console.log(new Uint8Array(hashBuffer)); // 二进制哈希值
});

六、常见误区与注意事项

误区正确理解
Array 就是二进制数组Array 是对象数组,不是原始二进制数据
ArrayBuffer 可以直接读写❌ 必须通过 TypedArrayDataView
TypedArrayArray 的子类⚠️ 行为像数组但不是 Array.isArray(view)false
字符串直接转 ArrayBuffer❌ 必须用 TextEncoder 进行 UTF-8 编码
所有设备字节序一样❌ 使用 DataView 可控制字节序,提高兼容性

七、性能优化提示

  • 避免频繁创建 ArrayBuffer:尽量复用。

  • 使用 TypedArray 视图而非逐字节操作:性能更高。

  • 批量处理数据:如一次读取 1024 字节比单字节循环快得多。

  • 避免不必要的复制:使用 .slice() 会拷贝,可以用 subarray() 获取视图(不拷贝内存)。

js
const view = new Uint8Array(buffer);
const sub = view.subarray(100, 200); // 共享原 buffer,不复制

总结:JavaScript 二进制体系全景图

text
                             +------------------+
                             |   Source Data    |
                             | (字符串、文件等) |
                             +--------+---------+
                                      |
                                      v
                          +-----------------------+
                          |    ArrayBuffer        |
                          | (原始字节缓冲区)       |
                          +-----------------------+
                                      |
                   +------------------+------------------+
                   |                                     |
                   v                                     v
     +--------------------------+         +------------------------------+
     |    TypedArray 视图        |         |      DataView 视图            |
     | (Int8Array, Float32Array) |         | (灵活读写,控制字节序)        |
     +--------------------------+         +------------------------------+
                   |                                     |
                   +------------------+------------------+
                                      |
                                      v
                             +------------------+
                             |  操作/传输/渲染   |
                             +------------------+

附:快速速查表

目的代码
创建 10 字节缓冲区new ArrayBuffer(10)
用整数填充缓冲区new Int8Array(buffer)
字符串 → 二进制new TextEncoder().encode(str).buffer
二进制 → 字符串new TextDecoder().decode(buffer)
文件转 ArrayBufferfile.arrayBuffer()
提取部分数据new Uint8Array(buffer, start, length)
控制字节序读写new DataView(buffer).getInt32(offset, littleEndian)

拓展阅读(现代 Web 二进制相关)

  • Blob:表示不可变的二进制数据对象,常用于文件。

  • File:继承自 Blob,表示用户上传的文件。

  • SharedArrayBuffer:多线程共享内存(配合 Web WorkerAtomics)。

  • WebGL:直接操作 GPU 内存,大量使用 TypedArray

  • Web Audio API:音频采样数据为 Float32Array


结语

JavaScript 虽然是高级语言,但通过 ArrayBuffer + TypedArray + DataView 三大组件,已具备强大的二进制数据处理能力,足以胜任现代 Web 中高性能、低延迟的多媒体、通信、加密等场景。

理解这套机制,是迈向高级前端/Node.js 开发、WebAssembly、音视频处理等领域的关键一步

如需我用一个完整示例演示“从字符串到 ArrayBuffer 到 Base64 再还原”的全流程,也可以继续提问!

本站总访问量 次 本站访客数 人次

1111111111111111111