javascript处理二进制之ArrayBuffer

5,843 阅读3分钟

最早javascript是不能处理二进制的,如果非要处理,只能用charCodeAt逐个地将字符串转成Unicode 编码的二进制数据。直到ECMAScript 5引入了blob,才使JS能真正可以处理二进制数据。

File API

blob又有一些衍生对象:File对象、FileList对象、URL对象、FileReader对象。

比如:

document.querySelector('input[name=picture]').onchange = function (e) {
    console.log(e.target.files[0]);
}

AJAX

很久之前,AJAX只能获取文本数据,XMLHttpRequest第二版允许服务器返回二进制数据。这时分成两种情况。如果明确知道返回的二进制数据类型,可以把返回类型(responseType)设为arraybuffer;如果不知道,就设为blob:

et xhr = new XMLHttpRequest();
xhr.open('GET', someUrl);
xhr.responseType = 'arraybuffer';

xhr.onload = function () {
  let arrayBuffer = xhr.response;
  // ···
};

xhr.send();

Canvas

Canvas元素输出的二进制元素是 TypedArray。

const ctx = canvas.getContext('2d');

const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const uint8ClampedArray = imageData.data;

为了配合以上的API,让JS更加好地处理二进制,ES6 将早就存在的ArrayBuffer对象、TypedArray视图和DataView视图纳入了 ECMAScript 规格。

ArrayBuffer代表了一段存储二进制数据的内存,是它的抽象层,比如 new ArrayBuffer(1024) ,我们就开辟了一段一个字节的内存。

但是你不能直接读写ArrayBuffer,而要用标准中的另外两种视图TypedArray或DataView。就想C语言中你不能直接处理内存,而需要将其转化成指针。

比如,如果我们使用TypedArray如下。ArrayBuffer一共有九种,且其数组成员必须是一个类型。我们先创建了一块有32个字节的内存区域buf,然后创建了一个指向buf的16位视图,开始于字节2,长度为2。第三个参数就是视图包含的数据个数,默认直到本段内存区域结束。

const buf = new ArrayBuffer(32);

const int = new Int16Array(buf, 2, 2);

我们也可以不通过ArrayBuffer,直接用TypedArray开辟一段内存。比如下面,生成了一个有八个成员的数组,每个成员8个字节,一共64个字节

const f64a = new Float64Array(8);

每个TypeArray实例都有一个BYTES_PER_ELEMENT属性代表字节数,如同ArrayBuffer的byteLength属性。length属性依然是数组的长度,普通数组的方法和属性,TypedArray 数组也完全适用。

TypedArray.prototype.slice(start=0, end=this.length)
TypedArray.prototype.copyWithin(target, start[, end = this.length]) 等

ArrayBuffer 本身只是一个 0 和 1 存放在一行里面的集合,它本身并不负责怎么分配。

(图片来源 —— A cartoon intro to ArrayBuffers and SharedArrayBuffers

用int8来存放:

用Unit16来存放:

我们从Int8数组里获取了元素 0下标的数据 和 1下标的数据,和在 Uint16数组 中0下标的数据是不同的,尽管他们都是同一段内存,是完全一样的二进制字节:

(图片来源 —— A cartoon intro to ArrayBuffers and SharedArrayBuffers

另外,我们会常用让字符串转换成Buffer,遍历字符串,并在创建的数组中存放对应字符的unicode编码:

  const buf = new ArrayBuffer(str.length * 2); // 每个字符占用2个字节
  const bufView = new Uint16Array(buf);    // 用js默认的16位无符号整数为单位
  for (let i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }