JS的二进制家族:ArrayBuffer的具体介绍 | 8月更文挑战

703 阅读3分钟

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

一、基础知识

1.1 ArrayBuffer

ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。ArrayBuffer 不能直接操作,而是要通过类型数组对象 或 DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。

ArrayBuffer 简单说是一片内存,但是你不能直接用它。这就好比你在 C 里面,malloc 一片内存出来,你也会把它转换成 unsigned_int32 或者 int16 这些你需要的实际类型的数组/指针来用。

这就是 JS 里的 TypedArray 的作用,那些 Uint32Array 也好,Int16Array 也好,都是给 ArrayBuffer 提供了一个 “View”,MDN 上的原话叫做 “Multiple views on the same data”,对它们进行下标读写,最终都会反应到它所建立在的 ArrayBuffer 之上。

语法
new ArrayBuffer(length)
  • 参数:length 表示要创建的 ArrayBuffer 的大小,单位为字节。
  • 返回值:一个指定大小的 ArrayBuffer 对象,其内容被初始化为 0。
  • 异常:如果 length 大于 Number.MAX_SAFE_INTEGER(>= 2 ** 53)或为负数,则抛出一个  RangeError  异常。
示例

下面的例子创建了一个 8 字节的缓冲区,并使用一个 Int32Array 来引用它:

let buffer = new ArrayBuffer(8);
let view   = new Int32Array(buffer);

从 ECMAScript 2015 开始,ArrayBuffer 对象需要用 new 运算符创建。如果调用构造函数时没有使用 new,将会抛出 TypeError  异常。比如执行该语句 let ab = ArrayBuffer(10) 将会抛出以下异常:

VM109:1 Uncaught TypeError: Constructor ArrayBuffer requires 'new'
    at ArrayBuffer (<anonymous>)
    at <anonymous>:1:10

对于一些常用的 Web API,如 FileReader API 和 Fetch API 底层也是支持 ArrayBuffer,这里我们以  FileReader API 为例,看一下如何把 File 对象读取为 ArrayBuffer 对象:

const reader = new FileReader();

reader.onload = function(e) {
  let arrayBuffer = reader.result;
}

reader.readAsArrayBuffer(file);

1.2 Unit8Array

Uint8Array 数组类型表示一个 8 位无符号整型数组,创建时内容被初始化为 0。创建完后,可以以对象的方式或使用数组下标索引的方式引用数组中的元素。

语法
new Uint8Array(); // ES2017 最新语法
new Uint8Array(length); // 创建初始化为0的,包含length个元素的无符号整型数组
new Uint8Array(typedArray);
new Uint8Array(object);
new Uint8Array(buffer [, byteOffset [, length]]);
示例
// new Uint8Array(length)
var uint8 = new Uint8Array(2);
uint8[0] = 42;
console.log(uint8[0]); // 42
console.log(uint8.length); // 2
console.log(uint8.BYTES_PER_ELEMENT); // 1

// new TypedArray(object)
var arr = new Uint8Array([21,31]);
console.log(arr[1]); // 31

// new Uint8Array(typedArray);
var x = new Uint8Array([2131]);
var y = new Uint8Array(x);
console.log(y[0]); // 21

// new Uint8Array(buffer [, byteOffset [, length]]);
var buffer = new ArrayBuffer(8);
var z = new Uint8Array(buffer, 14);

// new TypedArray(object)
// 当传入一个 object 作为参数时,就像通过 TypedArray.from() 
// 方法创建一个新的类型化数组一样。
var iterable = function*(){ yield* [1,2,3]; }(); 
var uint8 = new Uint8Array(iterable)
// Uint8Array[1, 2, 3]

通过ArrayBuffer的格式读取本地数据

document.getElementById('f').addEventListener('change', function (e) {
  const file = this.files[0];
  const fileReader = new FileReader();
  fileReader.onload = function () {
    const result = fileReader.result;
    console.log(result)
  }
  fileReader.readAsArrayBuffer(file);
}, false);

通过ArrayBuffer的格式读取Ajax请求数据

  • 通过xhr.responseType = "arraybuffer" 指定响应的数据类型
  • 在onload回调里打印xhr.response

前端

const xhr = new XMLHttpRequest();
xhr.open("GET", "ajax", true);
xhr.responseType = "arraybuffer";
xhr.onload = function () {
    console.log(xhr.response)
}
xhr.send();

Node端

const app = new Koa();
app.use(async (ctx) => {
  if (pathname = '/ajax') {
    ctx.body = 'hello world';
    ctx.status = 200;
  }
}).listen(3000)

ArrayBuffer转json

ws.onmessage = function (evt) {
	// evt.data是ArrayBuffer
    // 将其转换为uint8字节流
    var uint8_msg = new Uint8Array(evt.data);
    // 解码成字符串
    var decodedString = String.fromCharCode.apply(null, uint8_msg);
    console.log(decodedString); 
    // parse,转成json数据
    var data = JSON.parse(decodedString);
	console.log(data);
};

另一种方法,utf-8防止乱码

let content = file.data;//arraybuffer类型数据
let resBlob = new Blob([content])
let reader = new FileReader()
reader.readAsText(resBlob, "utf-8")
reader.onload = () => {
  let res = JSON.parse(reader.result)
  console.log(JSON.parse(reader.result))
}