Node.js Buffer与JavaScript TypeArray类型数组的异同

 2016年07月25日    256     声明


在ECMAScript 2015(ES6)推出TypeArray标准之前,JavaScript语言处理二进制数据非常困难,这在后端开发中使用很不方便。Node.js中的Buffer类就是为了解决二进制数据处理的问题,该类为Node.js带来了如TCP流操作和文件系统流操作的能力。ECMAScript 2015中TypeArray做为语言标准被引入,使JavaScript可以原生处理二进制数据。那么,在Node.js开发中我们是应该使用Buffer还是应该使用TypeArray呢?

  1. Buffer
  2. TypeArray-类型数组
  3. BufferTypeArray的比较

1. Buffer

Node.js是用于服务器端的语言,在服务器编程中二进制数据处理是必不可少的。Buffer类是为解决JavaScript处理二进制数据处理的问题,该类做为一个全局对象提供。Node.js中用于TCP网络处理的net模块、流处理模块stream、文件处理模块fs等都依赖于该模块进行底层的二进制数据处理。

Buffer的使用非常简单灵活。

如,使用16进制的数组创建缓存:

const buf = new Buffer([0x62,0x75,0x66,0x66,0x65,0x72]);
// 创建一个包含以下 ASCII 编码字符串的缓存
// ['b','u','f','f','e','r']

从另一个缓存中创建新缓存:

const buf1 = new Buffer('buffer');
const buf2 = new Buffer(buf1);

buf1[0] = 0x61;
console.log(buf1.toString()); // 'auffer'
console.log(buf2.toString()); // 'buffer' (未修改)

也可以从TypedArray.buffer属性或new ArrayBuffer()创建:

const arr = new Uint16Array(2);
arr[0] = 5000;
arr[1] = 4000;

const buf = new Buffer(arr.buffer); // 与arr共享内存;
console.log(buf); // <Buffer 88 13 a0 0f>

// TypdArray 修改后 Buffer 同样会修改
arr[1] = 6000;
console.log(buf); // <Buffer 88 13 70 17>

也可以不添加内容,创建一个空数组:

const buf = new Buffer(5);
console.log(buf); // <Buffer 78 e0 82 02 01< (每次值可能不一样)

使用字符串创建缓存时,可以指定字符编码;也可以通过toString()进行字符编码的转换:

const buf1 = new Buffer('this is a tést');
console.log(buf1.toString()); // this is a tést
console.log(buf1.toString('ascii')); // prints: this is a tC)st

const buf2 = new Buffer('7468697320697320612074c3a97374', 'hex');
console.log(buf2.toString()); // prints: this is a tést


2. TypeArray-类型数组

TypeArray(类型数组)在ECMAScript 2015中被加到语言标准中,它赋予了JavaScript直接读写二进制数据能力。TypeArray是一组类似数组的对象,它由ArrayBufferTypedArrayDataView三类对象构成:

  • ArrayBuffer是一个是一个用于表示二进制缓冲区(buffer)的对象,它并没有能力去访问和操作它自身的数据内容,要操作ArrayBuffer中的数据,需要创建一个Typed Array ViewDataView来读写缓存区内容。
  • Typed Array View即类型数组视图,是一组具有描述性的名字的不同类型的数组视图。如:Uint8ArrayInt8ArrayInt32ArrayFloat32Array
  • DataView提供了一些底层的API,通过这些API可以从ArrayBuffer中读/写数据。

ArrayBuffer可以用于创建一个指定长度的二进制数组,但该对象并不能直接读写,需要借助Typed Array ViewDataView读写其数据。

Typed Array View是一组不同类型的数组视图,它不同于DataViewDataView只能用于ArrayBuffer数据的读写,而不同类型的Typed Array View除了可以读写ArrayBuffer外,还可以单独使用。

如,创建一个ArrayBuffer并使用Typed Array ViewDataView读写数据:

var buffer = new ArrayBuffer(8);
var int32 = new Int32Array(buffer);

int32[0] = 42;
console.log(int32[0]); // 42

// 使用 DataView 读写
var dv = new DataView(buffer, 0);
dv.setInt16(1, 42);
dv.getInt16(1);

TypedArray也可以独立使用:

// 创建一个指定长度的 TypedArray
var uint8 = new Uint8Array(2);
uint8[0] = 42;
console.log(uint8[0]); // 42


3. BufferTypeArray的比较

JavaScript已原生支持二进制数据处理,那么我们应该直接使用原生的TypeArray还是应该使用Buffer处理二进制数据,二者之间有什么区别呢?

Buffer是对Uint8Array的实现

Buffer类实现了Uint8Array相关API。但Node对Buffer类进行了优化,其更适合在Node.js环境中使用。

Buffer并不完全兼容类型数组

Buffer同样是一个Uint8Array类型数组实例。但它与ES6中的类型数组规范并不完全兼容,如:ArrayBuffer#slice()会创建一个分隔部分数据的拷贝,而Buffer#slice()会创建一个从Buffer中拷贝数据的视图,相对来说Buffer#slice()更高效。

Buffer可以与类型数组共享内存区

可以从TypedArray.buffer属性或new ArrayBuffer()创建一个Buffer对象。该对象会与类型数组共享内存区:

const arr = new Uint16Array(2);
arr[0] = 5000;
arr[1] = 4000;

const buf1 = new Buffer(arr); // 复制 buffer
const buf2 = new Buffer(arr.buffer); // 与 arr 共享内存空间

console.log(buf1); // <Buffer 88 a0>,仅复制了两个元素
console.log(buf2); // <Buffer 88 13 a0 0f>