# [知識篇]JS Global Objects - ArrayBuffer

首先看到Buffer,有寫過NodeJS等後端語言,應該對此比較熟悉,而這邊只先了解一下 它是用來處理跟memory相關的效能議題,而在前端領域上,近來 Canvas、WebGL等開始走紅,也因此效能越來越受重視, 在這類互動應用中會需要與顯卡等系統有大量讀寫及計算,因此如果JS能夠不透過轉譯,直接操作binary data的話,效率肯定大幅提升。

在ES6中納入了包括ArrayBuffer在內的三個操作binary data的接口:

  • ArrayBuffer:Buffer,代表一段記憶體區塊,只能讀不能寫,僅能透過 View 操作其內容。
  • TypedArray:View,儲存固定型別資料的 Array,例如 Uint8Array(8-bit unsigned integer)、Float64Array(64-bit IEEE floating point number)。
  • DataView:View,不限制型別,可自定義從哪個 byte,以什麼型別,用哪種 byte order(endian)存取。

# ArrayBuffer

# construct

new ArrayBuffer(length)

定義一段固定大小的記憶體區塊,也稱為 byte-array。 主要的功能就是配置實體記憶體來儲存 raw binary data。 如果要操作其內容,僅能透過View(TypedArray , DataView)。

以下有幾種取得ArrayBuffer的方式:

// 配置16 bytes的記憶體區塊
const buf = new ArrayBuffer(16)

console.log(buf.byteLength) // 16
console.log(buf.slice(0, 2).byteLength) // 2

或是透過FileReader API 轉換File物件,得到使用者上傳的檔案數據。

const fileReader = new FileReader()

fileReader.addEventListener('load', e => console.log(e.result))
fileReader.readAsArrayBuffer(blob)

也能像Blob,設定Api response type。

var oReq = new XMLHttpRequest();
oReq.open("GET", "/myfile.png", true);
oReq.responseType = "arraybuffer"; //也能設為blob

oReq.onload = function (oEvent) {
  var arrayBuffer = oReq.response; // Note: not oReq.responseText
  if (arrayBuffer) {
    var byteArray = new Uint8Array(arrayBuffer);
    for (var i = 0; i < byteArray.byteLength; i++) {
      // do something with each byte in the array
    }
  }
};

oReq.send(null);

引用自MDN

# TypedArray

TypedArray其實不是一個api或是類別,而是泛指不同限定型別(typed)的陣列, 且都是用來操作binary data。 目前ES6定義了九種typed array types,詳細可以查看MDN,這邊就不一一介紹。

而幾個需要注意的點:

  • TypedArray只保存buffer的reference
var buf = new ArrayBuffer(64)
var int8 = new Uint8Array(buf) // [0....] byteLength = 64
var int16 = new Uint16Array(buf) // [0...] byteLength = 32
// new Uint32Array(buf) => byteLength = 16
// new Float64Array(buf) => byteLength = 8

int8[0] = 42
console.log(int8) // [42,0....]
console.log(int16) // [42,0....]
  • TypedArray 有著類似Array的methods,但會修改array長度的methods是沒有實作的如(pop/push...),畢竟是參考ArrayBuffer而其基本就是定義一個固定長度的記憶體區塊,所以只能讀寫內容但無法改變其長度。 
  • Blob也能接收buffer,來試試下面範例會印出什麼。
var buffer = new ArrayBuffer(2) // 2 bytes
var word = new Uint16Array(buffer)

// 'A'.charCodeAt() === 65
// 'B'.charCodeAt() === 66
var value = (65 << 8) + 66 // 使用位元運算子(<< 左移)並填入
word[0] = value

var blob = new Blob([buffer], {type: 'text/plain'})
var dataUri = window.URL.createObjectURL(blob)
window.open(dataUri)
// 轉換出來'BA'

# DataView

先來看看範例:

var buffer = new ArrayBuffer(2);
new DataView(buffer).setInt16(0, 256, true /* littleEndian */);
// Int16Array uses the platform's endianness.
console.log(new Int16Array(buffer)[0] === 256)

跟TypedArray不同的是,建構時不會固定其資料型別,只有當需要存取時透過getter/setter的方式,指定data type以及從哪個byte offset開始取出資料。

除此之外,另一個重點就是上面setter 函式後面多帶的一個boolean參數及標註的littleEndian,就是所謂的Endianness,主要會牽扯到與系統層面相關的讀寫效能,這邊就不深入研究。

這邊試試同上TypedArray的範例,看看有什麼不同~

var buffer = new ArrayBuffer(2) // array buffer for two bytes
var view = new DataView(buffer)

// 'A'.charCodeAt() === 65
// 'B'.charCodeAt() === 66
var value = (65 << 8) + 66 // 使用位元運算子(<< 左移)並填入
view.setUint16(0, value)

var blob = new Blob([buffer], {type: 'text/plain'})
var dataUri = window.URL.createObjectURL(blob)
window.open(dataUri)
// 轉換出來'AB'

從結果就能看出其中DataView與TypedArray的Endianness方式不同, 引用網路上資源解釋:

  • The TypedArray will use the native endianness of the system.
  • The DataView will default to big endian.

也因為這個差異,可以了解到,DataView因為能夠彈性指定 endian ,其在接收或是發送 Binary data 的應用場景上會比TypedArray安全許多,如通過 XMLHttpRequest, FileReader, 或是 任何 input/output API來交換資料的應用下,相對的,TypedArray因為 endian 預設是根據當下系統CUP的設定,在同個系統下的應用會比較適合,如Canvas或WebGL等與系統有著大量讀寫的互動應用。

# 支援度

# 總結

本章了解到:

  • JavaScript中如何透過ArrayBuffer/TypedArray/DataView來操作Binary Data
  • ArrayBuffer
    • Buffer,代表一段記憶體區塊,只能讀不能寫,僅能透過 View 操作其內容。
    • 跟blob相同,在request中能夠指定responseType回傳arraybuffer型別。
  • TypedArray
    • View,儲存固定型別資料的 Array,例如 Uint8Array(8-bit unsigned integer)、Float64Array(64-bit IEEE floating point number)。
    • 共九種typed array
    • 適合在Canvas或WebGL的應用上用來創建與操作Binary data。
  • DataView
    • View,不限制型別,可自定義從哪個 byte,以什麼型別,用哪種 byte order(endian)存取。
    • 適合需要安全處理Binary data的情境,如從server接收或是發送到其他系統的應用。

額外:

如要更深入了解,記憶體操作,以下相關知識就一定要去瞭解看看:

  • Endianness
  • Data Structure Alignment

# 參考