當 Webapp 有了視頻、音頻操作,及用
WebSocket
存取原始資料等等的功能而變得越來越強大,讓 JavaScript 代碼可以快速、簡單地操作原始二進制資料的好處就越來越明顯。以前唯一的解法是視原始資料為
字串
並用
charCodeAt()
方法讀取資料緩衝的位元組。
然而,由於需要多次型別轉換(特別是二進制資料並非以位元組計算,如 32 位元整數或浮點數),這個解法既慢又容易發生錯誤。
JavaScript 型別陣列提供了存取二進制資料更有效率的機制。
型別陣列不該與一般的陣列搞混,當對型別陣列呼叫
Array.isArray()
時會回傳
false
。此外,一般陣列所提供的方法並非全部被型別陣列所支援(如 push 以及 pop 方法)
為了追求最大的可朔性與效率,JavaScript 型別陣列的實作分為
緩衝
與
視圖
。一個緩衝(以類別
ArrayBuffer
實作)為代表一塊資料資料的物件,它沒有任何格式,也沒有任何存取其內容的機制。想存取一個緩衝所佔的記憶體必須用一個視圖。一個視圖提供了一種前後關係 — 資料型別、起始偏移與元素的數目 — 使得資料變成真實的型別陣列。視圖以類別
ArrayBufferView
與其子類別實作。
ArrayBuffer
是一種資料型態,用於表示通用的固定長度二進制資料緩衝區。 你不能直接操作
ArrayBuffer
的內容。但是,你可以建立一個型別陣列視圖 (typed array view) 或一個
DataView
,它以特定格式代表緩衝區,並使用它讀取和寫入緩衝區的內容。
型別陣列視圖具有自述性名稱,並為所有常用的數字類型(如
Int8
,
Uint32
,
Float64
等)提供視圖。 有一個特殊的型別陣列視圖
Uint8ClampedArray
。 它的範圍值在 0 到 255 之間。它對於
Canvas 的資料處理
非常有用。
Value Range
Size in bytes
Description
Web IDL type
Equivalent C type
1.2x10^-38 to 3.4x10^38
32-bit IEEE floating point number ( 7 significant digits e.g. 1.1234567)
unrestricted float
float
Float64Array
5.0x10^-324 to 1.8x10^308
64-bit IEEE floating point number (16 significant digits e.g. 1.123...15)
unrestricted double
double
The
DataView
is a low-level interface that provides a getter/setter API to read and write arbitrary data to the buffer. This is useful when dealing with different types of data, for example. Typed array views are in the native byte-order (see
Endianness
) of your platform. With a
DataView
you are able to control the byte-order. It is big-endian by default and can be set to little-endian in the getter/setter methods.
FileReader.prototype.readAsArrayBuffer()
The
FileReader.prototype.readAsArrayBuffer()
method starts reading the contents of the specified
Blob
or
File
.
XMLHttpRequest.prototype.send()
XMLHttpRequest
instances'
send()
method now supports typed arrays and
ArrayBuffer
objects as argument.
ImageData.data
Is a
Uint8ClampedArray
representing a one-dimensional array containing the data in the RGBA order, with integer values between
0
and
255
inclusive.
先來建立一個 16 位元組的緩衝:
var buffer = new ArrayBuffer(16);
在這個時候,我們有位元組全部初始為 0 的一塊記憶體,但是用它做不了什麼事。我們只能確認它的確是 16 的位元組的長度:
if (buffer.byteLength === 16) {
console.log("沒錯,是 16 個位元組。");
} else {
console.log("糟糕,長度不對!");
要用這個緩衝搞點花樣之前,建立一個視圖是必需的。來建立一個視圖把這個緩衝當作 32 位元的有符號整數:
var int32View = new Int32Array(buffer);
這樣就可以把它當作一般的陣列以存取欄位:
for (var i = 0; i < int32View.length; i++) {
int32View[i] = i * 2;
這會把此陣列的四個欄位以 0、2、4、6 填滿(四個 4 位元組,總共 16 位元組)。
考慮在同一份資料上建立多個視圖的有趣情形。舉例來說,繼續使用上面的代碼:
var int16View = new Int16Array(buffer);
for (var i = 0; i < int16View.length; i++) {
console.log("Entry " + i + ": " + int16View[i]);
雖然該緩衝上已有一個 32 位元的視圖,這裡建立了同一個緩衝上的 16 位元整數視圖,這裡的輸出為 0, 0, 2, 0, 4, 0, 6, 0。
繼續考慮這種情況:
int16View[0] = 32;
console.log("現在32位元陣列的欄位0是" + int32View[0]);
輸出為"現在 32 位元陣列的欄位 0 是 32"。也就是,這兩個陣列真的是同一個資料緩衝的在不同格式下的看法。其他 view types 也是同樣的情形。
在單一個緩衝使用不同型別、不同起始偏移的多個視圖以操作資料物件含有的多個資料型別。這個方法可以用在使用 WebGL 、資料檔案、js-ctypes 時遇到的複雜的資料結構。
考慮這個 C 結構:
struct someStruct {
unsigned long id;
char username[16];
float amountDue;
可以用以下方法存取含有這種資料格式的緩衝:
var buffer = new ArrayBuffer(24);
// ... 將資料讀入緩衝 ...
var idView = new Uint32Array(buffer, 0, 1);
var usernameView = new Uint8Array(buffer, 4, 16);
var amountDueView = new Float32Array(buffer, 20, 1);
舉例來說,可以用 amountDueView[0]
存取 amountDue。
C 結構的資料結構對齊是與使用平台有關,須小心這些填充上的差異。
After processing a typed array, it is sometimes useful to convert it back to a normal array in order to benefit from the Array
prototype. This can be done using Array.from
, or using the following code where Array.from
is unsupported.