Node.js Buffer 与编码:二进制与字符集

HTTP 头、文件读写、加密、网络协议——最后都会落到 字节序列。在 JavaScript 里字符串是 UTF-16,字节则主要靠 Buffer(以及 TypedArray)来表达。
这一篇想讲清楚几件事:Buffer 和字符串怎么互转、编码参数什么时候不能省略、以及和 Stream 拼在一起时容易在哪一步混单位

BufferUint8Array 的子类(在较新 Node 里对齐得更紧),表示一段 可变的二进制数据

  • 下标访问是 0–255 的字节
  • 可以和 字符串TypedArrayArrayBuffer 互相转换或 共享底层内存Buffer.from 的拷贝行为要看重载)。

和「普通 JS 字符串」的分工可以记:

  • 字符串:给人看的、带字符集语义的文本;
  • Buffer:给协议、磁盘、socket 看的 原始字节

常见 API:

  • Buffer.from(str, encoding):字符串 → Buffer;
  • buf.toString(encoding):Buffer → 字符串。

encoding 省略时往往默认 utf8,但:

  • 处理 非 UTF-8 来源(老系统、部分二进制协议伪装成文本)时,乱码或截断 会悄悄发生;
  • base64 / hex 常用于 可打印表示,不是「另一种文本编码」那么简单。

实践:

  • 边界上(读文件、解包网络帧)明确写 encoding 或明确当 二进制 处理;
  • 日志里打印 Buffer 前先 toString 或截断,避免巨量十六进制刷屏。
  • buffer.subarray(start, end)共享同一块底层存储,改切片会影响原 Buffer(要小心);
  • Buffer.from 某些重载会 拷贝,有的会 包装现有内存——以文档为准;
  • 新建 Buffer 优先 Buffer.alloc(清零),避免 Buffer.allocUnsafe 泄漏旧内存内容(除非性能敏感且立即覆写)。

安全敏感场景(密钥、token):

  • 用完后 覆写或交给 GC,不要长期挂在全局对象上。

data 事件 默认可能是 Buffer(除非设了编码把流变成字符串模式)。

含义:

  • 按字节处理(定长协议)时保持 Buffer 管道
  • 按行处理文本 时可以在合适层 setEncoding('utf8') 或自己 按 Buffer 解码

和 Stream 篇合起来看:

  • 背压 管的是「流不爆」;编码 管的是「字符不切半」——两个问题都要有人管。

若你在做:

  • 二进制协议解析与 WASM 交互与原生 addon 交换内存

会更多碰到 ArrayBuffer / DataView / 具体 TypedArray。和 Buffer 的关系可以粗记:

  • Buffer 是 Node 里最好用的字节容器
  • TypedArray 更贴近 ECMAScript 标准浏览器 侧代码。

选型:Node 内部优先 Buffer;要 跨环境复用算法 时再往 TypedArray 靠。

  • Buffer 表示字节;字符串表示 已解码的文本(在已知编码前提下);
  • 互转必带编码意识,默认 UTF-8 不是万能;
  • 切片共享内存,协议解析时注意别名与拷贝;
  • Stream 搭配时,分清 二进制模式字符串模式

把单位搞清楚,后面看 crypto、压缩、序列化格式,会少一半「为什么多了个乱码」的调试时间。