← [ BACK_TO_NOTES ]

Node.js Buffer

Node.js Buffer 是二进制缓冲区概念在 Node.js 中的具体类型与 API;本页聚焦分配、编码、拼接、切片和数值读写语义。

#concept #growing #backend #nodejs #javascript

[!info] related notes

Node.js Buffer

一句话定义

Buffer 是 Node.js 提供的 Uint8Array 子类,用来处理字节数据,并额外提供编码转换、数值读写、拼接等 Node.js API。

角色边界

Buffer(二进制缓冲区) 维护“有限字节区域”的通用心智模型;本页只维护 Node.js 的具体实现语义。因此“Buffer 和 Stream 为什么是不同抽象”优先回到上位概念,本页重点回答:

  • Node.js 怎样创建字节存储;
  • 字符串怎样按指定编码转成字节、再解码回来;
  • slice() / subarray() 的共享存储意味着什么;
  • allocUnsafe() 为什么要求调用方确保相关字节被覆盖;
  • 多字节整数怎样受大小端约束。

Buffer vs Uint8Array

特性BufferUint8Array
继承关系Buffer 继承自 Uint8ArrayTypedArray 原生类型
底层存储使用 ArrayBuffer backing store,并由 Node/V8 跟踪外部内存占用使用 ArrayBuffer backing store
编码支持内置字符串编码转换本身不提供 Node Buffer 的编码便利 API
使用重点Node I/O 与二进制协议处理Web/ECMAScript 通用 typed-array 视图
API丰富(read/write 系列)标准 TypedArray

Buffer 是 Uint8Array 的子类,可以当 Uint8Array 使用,但有额外的编码和便利方法。

创建 Buffer

// 推荐:指定大小分配并清零
const buf = Buffer.alloc(10);        // 10 字节,全部填充 0
const bufUnsafe = Buffer.allocUnsafe(10);  // 10 字节,可能包含旧数据,使用前必须覆盖

// 从字符串创建
const bufStr = Buffer.from('hello', 'utf8');
const bufBase64 = Buffer.from('aGVsbG8=', 'base64');
const bufHex = Buffer.from('68656c6c6f', 'hex');

// 从数组/ArrayBuffer 创建
const bufArr = Buffer.from([0x68, 0x65, 0x6c, 0x6c, 0x6f]);
const bufAb = Buffer.from(new ArrayBuffer(10));

编码支持

Buffer 支持以下编码:utf8、ascii、latin1、base64、base64url、hex、utf16le

const buf = Buffer.from('你好', 'utf8');
console.log(buf.length);           // 6(此例两个字符各占 3 个 UTF-8 字节)
console.log(buf.toString('hex'));   // e4bda0e5a5bd
console.log(buf.toString('base64')); // 5L2g5aW9

// 指定起始偏移与结束偏移(不包含结束位置)解码
const partial = buf.toString('utf8', 0, 3); // '你'

拼接 Buffer

// concat:合并多个 Buffer
const buf1 = Buffer.from('Hello');
const buf2 = Buffer.from(' ');
const buf3 = Buffer.from('World');
const merged = Buffer.concat([buf1, buf2, buf3]);
console.log(merged.toString()); // 'Hello World'

// 指定总长度
const mergedWithLen = Buffer.concat([buf1, buf2, buf3], 11);

slice vs subarray

const buf = Buffer.from('Hello World');

// slice:返回视图(共享内存!修改子切片会影响原 Buffer)
const sliced = buf.slice(0, 5);
sliced[0] = 0x68; // 原 buf 变成 'hello World'

// subarray:和 slice 行为一致,也是共享内存
const sub = buf.subarray(0, 5);

// 如果需要独立副本,手动复制
const copy = Buffer.from(buf.subarray(0, 5));

关键区别:

  • slice 和 subarray 都返回共享内存的视图
  • 修改视图会影响原始 Buffer
  • 需要独立副本时用 Buffer.from(view) 创建

读写二进制数据

const buf = Buffer.alloc(11);

// 写入不同类型的数值
buf.writeUInt8(0xFF, 0);           // 无符号 8 位
buf.writeUInt16BE(0x1234, 1);      // 大端序 16 位
buf.writeUInt32LE(0x12345678, 3);  // 小端序 32 位
buf.writeFloatBE(3.14, 7);         // 浮点数

// 读取
console.log(buf.readUInt8(0));        // 255
console.log(buf.readUInt16BE(1));     // 4660
console.log(buf.readUInt32LE(3));     // 305419896

内存注意事项

// allocUnsafe 可能包含旧数据(来自已释放的内存)
// 在读取、发送或暴露这些字节前,确保全部覆盖
const fast = Buffer.allocUnsafe(1024 * 1024);
fast.fill(0); // 手动清零

// 字节存储计入外部内存;Buffer 包装对象仍由 V8 管理
// --max-old-space-size 不是整个进程或 Buffer 字节存储的内存上限
console.log(Buffer.poolSize); // 默认 8192 字节

常见误区

  • allocUnsafe 的“unsafe”指返回的内存未初始化;只有在后续逻辑确保相关字节全部被覆盖时才适合使用。
  • slice/subarray 返回共享底层存储的视图,不是独立副本。
  • 大端 vs 小端必须服从具体协议或文件格式,不能仅凭 CPU 架构猜测外部数据格式。
  • 字符串编码必须在字节与文本两侧保持一致;同一组字节用不同编码解码会得到不同结果。
  • Buffer 是 Uint8Array 的 Node.js 扩展,不应把 Uint8Array 当成本页别名。

学习检查

  1. 为什么 Node.js Buffer 可以传给期待 Uint8Array 的许多 API?反方向是否总成立?
  2. Buffer.from(view) 与 buf.subarray() 在“复制还是共享底层存储”上有什么差异?
  3. 什么条件下使用 allocUnsafe() 才不会把旧内存内容暴露到输出?
  4. 为什么协议字段必须明确 BE/LE,而不是依赖当前机器的 CPU 端序?
  5. 如果你只是想解释 Buffer 的通用概念,为什么应该先读 Buffer(二进制缓冲区) 而不是本页?

参考

创建于 2026/5/27 更新于 2026/10/6
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本