ArrayBuffer


如果要儲存一組二進位資料,可以使用 ArrayBuffer,它從 ES6 之後成為標準之一,建構 ArrayBuffer 實例時,必須指定位元組長度,例如:

let buf = new ArrayBuffer(32); // 32 個位元組
console.log(buf.byteLength);  // 顯示 32

你沒辦法直接改變 ArrayBuffer 實例的內容,只能透過 byteLength 特性取得長度,slice 方法切割 ArrayBuffer

若要改變或取得 ArrayBuffer 內容,方式之一是透過 TypedArray,有以下幾個子類型:

  • Int8Array:8 位有號整數,長度一位元組
  • Uint8Array:8 位無號整數,長度一位元組
  • Uint8ClampedArray:8 位無號整數,長度一位元組,超出 [0, 255] 的值,會設定為 0 或 255。
  • Int16Array:16 位有號整數,長度二位元組。
  • Uint16Array:16 位無號整數,長度二位元組。
  • Int32Array:32 位有號整數,長度四位元組。
  • Uint32Array:32 位無號整數,長度四位元組。
  • Float32Array:32 位浮點數,長度四位元組。
  • Float64Array:64 位浮點數,長度八位元組。

不同的 TypedArray 子類型,檢視、操作 ArrayBuffer 的觀點(View)不同,例如同一個 ArrayBuffer 實例,在不同的 TypedArray 觀點下,每個元素的位元長度不同,因而透過 length 取得元素個數也就不同:

let buf = new ArrayBuffer(32);
let i8arr = new Int8Array(buf);
let i16arr = new Int16Array(buf);
let i32arr = new Int32Array(buf);

console.log(i8arr.length);  // 顯示 32
console.log(i16arr.length); // 顯示 16
console.log(i32arr.length); // 顯示 8

buf 有 32 個位元組,也就是 256 個位元,就 Int8Array 來說,8 個位元為一個元素,因此會有 32 個元素,就 Int16Array 來說,16 位元為一個元素,因而會有 16 個元素,同樣地,對 Int32Array 來說,會有 8 個元素。

在設定元素時,對每個元素的觀點,也視不同的 TypedArray 子類型而不同,例如,透過 Uint8Array 來寫入與讀出:

let buf = new ArrayBuffer(5);
let ui8arr = new Uint8Array(buf);
ui8arr[0] = 72;  // H
ui8arr[1] = 101; // e
ui8arr[2] = 108; // l
ui8arr[3] = 108; // l
ui8arr[4] = 111; // 0

// 顯示 Hello
console.log(String.fromCharCode.apply(null, ui8arr));

在建構 TypedArray 子類型時,也可以使用類陣列物件,例如:

let ui8arr = new Uint8Array([72, 101, 108, 108, 111]);

// 顯示 Hello
console.log(String.fromCharCode.apply(null, ui8arr));

在建構 TypedArray 之後,操作方法和特性,與 Array 幾乎是完全相同的,可參考 TypedArray API 文件

TypedArray 的子類型,對待每個元素的觀點是一致的,如果資料中包含了不同類型的元素,例如,同時包含了 8 位整數與 16 位整數,可以使用 DataView 來處理:

let buf = new ArrayBuffer(3);
let dataView =  new DataView(buf);

dataView.setInt16(0, 72);
dataView.setInt8(2, 105);

console.log(dataView.getInt16(0));
console.log(dataView.getInt8(2));

由於資料長度不固定,在呼叫 setXXXgetXXX 時,必須指定位元組偏移量。