轉:http://es6.ruanyifeng.com/#docs/arraybuffer
ArrayBuffer
ArrayBuffer
對象、TypedArray
視圖和DataView
視圖是 JavaScript 操作二進制數據的一個接口。這些對象早就存在,屬於獨立的規格(2011 年 2 月發布),ES6 將它們納入了 ECMAScript 規格,並且增加了新的方法。它們都是以數組的語法處理二進制數據,所以統稱為二進制數組。
這個接口的原始設計目的,與 WebGL 項目有關。所謂 WebGL,就是指瀏覽器與顯卡之間的通信接口,為了滿足 JavaScript 與顯卡之間大量的、實時的數據交換,它們之間的數據通信必須是二進制的,而不能是傳統的文本格式。文本格式傳遞一個 32 位整數,兩端的 JavaScript 腳本與顯卡都要進行格式轉化,將非常耗時。這時要是存在一種機制,可以像 C 語言那樣,直接操作字節,將 4 個字節的 32 位整數,以二進制形式原封不動地送入顯卡,腳本的性能就會大幅提升。
二進制數組就是在這種背景下誕生的。它很像 C 語言的數組,允許開發者以數組下標的形式,直接操作內存,大大增強了 JavaScript 處理二進制數據的能力,使得開發者有可能通過 JavaScript 與操作系統的原生接口進行二進制通信。
二進制數組由三類對象組成。
(1)ArrayBuffer
對象:代表內存之中的一段二進制數據,可以通過“視圖”進行操作。“視圖”部署了數組接口,這意味着,可以用數組的方法操作內存。
(2)TypedArray
視圖:共包括 9 種類型的視圖,比如Uint8Array
(無符號 8 位整數)數組視圖, Int16Array
(16 位整數)數組視圖, Float32Array
(32 位浮點數)數組視圖等等。
(3)DataView
視圖:可以自定義復合格式的視圖,比如第一個字節是 Uint8(無符號 8 位整數)、第二、三個字節是 Int16(16 位整數)、第四個字節開始是 Float32(32 位浮點數)等等,此外還可以自定義字節序。
簡單說,ArrayBuffer
對象代表原始的二進制數據,TypedArray 視圖用來讀寫簡單類型的二進制數據,DataView
視圖用來讀寫復雜類型的二進制數據。
TypedArray 視圖支持的數據類型一共有 9 種(DataView
視圖支持除Uint8C
以外的其他 8 種)。
數據類型 | 字節長度 | 含義 | 對應的 C 語言類型 |
---|---|---|---|
Int8 | 1 | 8 位帶符號整數 | signed char |
Uint8 | 1 | 8 位不帶符號整數 | unsigned char |
Uint8C | 1 | 8 位不帶符號整數(自動過濾溢出) | unsigned char |
Int16 | 2 | 16 位帶符號整數 | short |
Uint16 | 2 | 16 位不帶符號整數 | unsigned short |
Int32 | 4 | 32 位帶符號整數 | int |
Uint32 | 4 | 32 位不帶符號的整數 | unsigned int |
Float32 | 4 | 32 位浮點數 | float |
Float64 | 8 | 64 位浮點數 | double |
注意,二進制數組並不是真正的數組,而是類似數組的對象。
很多瀏覽器操作的 API,用到了二進制數組操作二進制數據,下面是其中的幾個。
- File API
- XMLHttpRequest
- Fetch API
- Canvas
- WebSockets
ArrayBuffer 對象
概述
ArrayBuffer
對象代表儲存二進制數據的一段內存,它不能直接讀寫,只能通過視圖(TypedArray
視圖和DataView
視圖)來讀寫,視圖的作用是以指定格式解讀二進制數據。
ArrayBuffer
也是一個構造函數,可以分配一段可以存放數據的連續內存區域。
const buf = new ArrayBuffer(32);
上面代碼生成了一段 32 字節的內存區域,每個字節的值默認都是 0。可以看到,ArrayBuffer
構造函數的參數是所需要的內存大小(單位字節)。
為了讀寫這段內容,需要為它指定視圖。DataView
視圖的創建,需要提供ArrayBuffer
對象實例作為參數。
const buf = new ArrayBuffer(32); const dataView = new DataView(buf); dataView.getUint8(0) // 0
上面代碼對一段 32 字節的內存,建立DataView
視圖,然后以不帶符號的 8 位整數格式,從頭讀取 8 位二進制數據,結果得到 0,因為原始內存的ArrayBuffer
對象,默認所有位都是 0。
另一種 TypedArray 視圖,與DataView
視圖的一個區別是,它不是一個構造函數,而是一組構造函數,代表不同的數據格式。
const buffer = new ArrayBuffer(12); const x1 = new Int32Array(buffer); x1[0] = 1; const x2 = new Uint8Array(buffer); x2[0] = 2; x1[0] // 2
上面代碼對同一段內存,分別建立兩種視圖:32 位帶符號整數(Int32Array
構造函數)和 8 位不帶符號整數(Uint8Array
構造函數)。由於兩個視圖對應的是同一段內存,一個視圖修改底層內存,會影響到另一個視圖。
TypedArray 視圖的構造函數,除了接受ArrayBuffer
實例作為參數,還可以接受普通數組作為參數,直接分配內存生成底層的ArrayBuffer
實例,並同時完成對這段內存的賦值。
const typedArray = new Uint8Array([0,1,2]); typedArray.length // 3 typedArray[0] = 5; typedArray // [5, 1, 2]
上面代碼使用 TypedArray 視圖的Uint8Array
構造函數,新建一個不帶符號的 8 位整數視圖。可以看到,Uint8Array
直接使用普通數組作為參數,對底層內存的賦值同時完成。
ArrayBuffer.prototype.slice()
ArrayBuffer
實例有一個slice
方法,允許將內存區域的一部分,拷貝生成一個新的ArrayBuffer
對象。
const buffer = new ArrayBuffer(8); const newBuffer = buffer.slice(0, 3);
上面代碼拷貝buffer
對象的前 3 個字節(從 0 開始,到第 3 個字節前面結束),生成一個新的ArrayBuffer
對象。slice
方法其實包含兩步,第一步是先分配一段新內存,第二步是將原來那個ArrayBuffer
對象拷貝過去。
slice
方法接受兩個參數,第一個參數表示拷貝開始的字節序號(含該字節),第二個參數表示拷貝截止的字節序號(不含該字節)。如果省略第二個參數,則默認到原ArrayBuffer
對象的結尾。
除了slice
方法,ArrayBuffer
對象不提供任何直接讀寫內存的方法,只允許在其上方建立視圖,然后通過視圖讀寫。