最早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);
}
复制代码