前段時間一直在研究 Web Audio API 以及語音通訊相關的知識,內容側重於音頻流在 AudioContext 各個節點之間的流動狀況,而如今要摸清楚音頻到流底是個什麼樣的數據格式,因此對 ArrayBuffer 的研究就顯得格外重要了。javascript
1、Array 在內存中的堆棧模型html
1. Array 的獲取前端
Javascript 中如何產生 Array:html5
[element0, element1, ..., elementN] new Array(element0, element1, ..., elementN) new Array(arrayLength)
直接定義,或者經過構造函數建立一個 Array,固然也可使用其餘的手段:java
"array".split(""); "array".match(/a|r/g);
等等,方式有不少。可是 Array 內部是個什麼樣的結構,恐怕不少人還不是很清楚。linux
2. 堆棧模型ios
在數組中咱們能夠放不少不一樣數據類型的數據,如:web
var arr = [21, "李靖", new Date(), function(){}, , null];
上面這個數組中一次放入了 數字、字符串、對象、函數、undefined 和 null,對於上面的數據接口咱們能夠具象的描述下:chrome
棧 +---------+ 堆 | 21 | +-------------------+ +---------+ | | | "李靖" | | | +---------+ | +--------+ | | [refer] |----------->| Object | | +---------+ | +--------+ | | [refer] |----------------->+--------+ | +---------+ | |function| | |undefined| | +--------+ | +---------+ | | | null | +-------------------+ +---------+ Created By Barret Lee
JavaScript 的數據類型分爲兩種,一種是值類型,一種是引用類型,常見的引用類型有 Object 和 Array,數組的儲存模型中,若是是諸如 Number、String 之類的值類型數據會被直接壓入棧中,而引用類型只會壓入對該值的一個索引,用 C 語言的概念來解釋就是隻保存了數據的指針,這些數據是儲存在堆中的某塊區間中。棧堆並非獨立的,棧也能夠在堆中存放。數組
好了,對 Array 的說明就到這裏,下面具體說說 ArrayBuffer 的相關知識。
2、ArrayBuffer
web 是個啥玩意兒,web 要討論的最基本問題是什麼?我以爲有兩點,一個是數據,一個是數據傳輸,至於數據的展現,紛繁複雜,這個應該是 web 上層的東西。而本文要討論的 ArrayBuffer 就是最基礎的數據類型,甚至不能稱之爲數據類型,它是一個數據容易,須要經過其餘方式來讀寫。
官方點的定義:
The ArrayBuffer is a data type that is used to represent a generic, fixed-length binary data buffer. You can't directly manipulate the contents of an ArrayBuffer; instead, you create an ArrayBufferView object which represents the buffer in a specific format, and use that to read and write the contents of the buffer.
表示二進制數據的原始緩衝區,該緩衝區用於存儲各類類型化數組的數據。 沒法直接讀取或寫入 ArrayBuffer,但可根據須要將其傳遞到類型化數組或 DataView 對象 來解釋原始緩衝區。
他是一個二進制數據的原始緩衝區,雖然 JavaScript 是弱類型語言,可是他自己是對數據的類型和大小都有限制的,咱們須要經過某種數據結構將緩衝區的內容有序的讀取出來(寫進去)。
1. 原始緩衝區的建立
經過 ArrayBuffer 這個構造函數能夠建立一個原始緩衝區:
var buffer = new ArrayBuffer(30);
從 chrome 控制檯能夠看到:
[![arrayBuffer]({{ site.repo }}/images/blog-article-images/blog/audio/arraybuffer.jpg)]({{ site.repo }}/images/blog-article-images/blog/audio/arraybuffer.jpg)
buffer 實例擁有一個 byteLength 的屬性,用於獲取 buffer 的 size,一個只有 IE11+ 以及 ios6+ 支持的 slice 方法,用於對 buffer 長度進行截取操做。
ArrayBuffer slice( unsigned long begin unsigned long end Optional );
能夠測試這個 DEMO:
var buffer = new ArrayBuffer(12); var x = new Int32Array(buffer); x[1] = 1234; var slice = buffer.slice(4); var y = new Int32Array(slice); console.log(x[1]); console.log(y[0]); x[1] = 6789; console.log(x[1]); console.log(y[0]);
2. 類型化數組
類型化數組類型表示可編制索引和操縱的 ArrayBuffer 對象 的各類視圖。 全部數組類型的長度均固定。
名稱 | 大小(以字節爲單位) | 描述 |
---|---|---|
Int8Array | 1 | 8 位二補碼有符號整數 |
Uint8Array | 1 | 8 位無符號整數 |
Int16Array | 2 | 16 位二補碼有符號整數 |
Uint16Array | 2 | 16 位無符號整數 |
Int32Array | 4 | 32 位二補碼有符號整數 |
Uint32Array | 4 | 32 位無符號整數 |
Float32Array | 4 | 32 位 IEEE 浮點數 |
Float64Array | 8 | 64 位 IEEE 浮點數 |
Int 就是整型,Uint 爲無符號整形,Float 爲浮點型,這些是 C 語言中的基本概念,我就不具體解釋了。因爲這些視圖化結構都是大同小異,本文只對 Float32Array 類型做說明,讀者能夠觸類旁通。
Float32Array 跟 Array 是十分相似的,只不過他每個元素都是都是一個 32位(4字節) 的浮點型數據。Float32Array 一旦建立其大小不能再修改。
咱們能夠直接建立一個 Float32Array:
var x = new Float32Array(2); x[0] = 17; console.log(x[0]); // 17 console.log(x[1]); // 0 console.log(x.length); // 2
須要有這麼一個概念,他依然是一個數組,只不過該數組中的每一個元素都是 Float 32 位的數據類型,再如:
var x = new Float32Array([17, -45.3]); console.log(x[0]); // 17 console.log(x[1]); // -45.29999923706055 console.log(x.length); // 2
咱們把一個數組的值直接賦給了 x 這個 Float32Array 對象,那麼在儲存以前會將它轉換成一個 32位浮點數。
因爲該類數組的每一個元素都是同一類型,因此在堆棧模型中,他們所有會被壓入到棧之中,所以類型化數組都是值類型,他並非引用類型!這個要引發注意,從下面的例子中也能夠反映出來:
var x = new Float32Array([17, -45.3]); var y = new Float32Array(x); console.log(x[0]); // 17 console.log(x[1]); //-45.29999923706055 console.log(x.length); // 2 x[0] = -2; console.log(y[0]); // 17, y的值沒變
將 x 的值複製給 y,修改 x[0], y[0] 並無變化。
除了上面的方式,咱們還能夠經過其餘方式來建立一個類型化數組:
var buffer = new ArrayBuffer(12); var x = new Float32Array(buffer, 0, 2); var y = new Float32Array(buffer, 4, 1); x[1] = 7; console.log(y[0]); // 7
解釋下這裏爲何返回 7.
ArrayBuffer(12) +-+-+-+-+-+-+-+-+-+-+-+-+-+ |0|1|2|3|4|5|6|7|8| | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+ \ / x (Float32Array) offset:0 byteLength:4 length:2 ArrayBuffer(12) +-+-+-+-+-+-+-+-+-+-+-+-+-+ |0|1|2|3|4|5|6|7|8| | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+ \ / y
看了上面的圖解還有疑問麼?我以爲我不用繼續解釋了。能夠把 ArrayBuffer 的單位當作 1,而 Float32Array 的單位是 4.
3. DataView對象
DataView 對象對數據的操做更加細緻,不過我以爲沒啥意思,上面提到的各類類型化數組已經能夠基本知足應用了,因此這裏就一筆帶過,一個簡單的示例:
var buffer = new ArrayBuffer(12); var x = new DataView(buffer, 0); x.setInt8(0, 22); x.setFloat32(1, Math.PI); console.log(x.getInt8(0)); // 22 console.log(x.getFloat32(1)); // 3.1415927410125732
3、XHR2 中的 ArrayBuffer
ArrayBuffer 的應用特別普遍,不管是 WebSocket、WebAudio 仍是 Ajax等等,前端方面只要是處理大數據或者想提升數據處理性能,那必定是少不了 ArrayBuffer 。
XHR2 並非什麼新東西,可能你用到了相關的特性,殊不知這就是 XHR2 的內容。最主要的一個東西就是 「xhr.responseType」,他的做用是設置響應的數據格式,可選參數有:"text"、"arraybuffer"、"blob"或"document"。請注意,設置(或忽略)xhr.responseType = '' 會默認將響應設爲"text"。這裏存在一個這樣的對應關係:
請求 響應 text DOMString arraybuffer ArrayBuffer blob Blob document Document
舉個例子:
var xhr = new XMLHttpRequest(); xhr.open('GET', '/path/to/image.png', true); xhr.responseType = 'arraybuffer'; xhr.onload = function(e) { // this.response == uInt8Array.buffer var uInt8Array = new Uint8Array(this.response); }; xhr.send();
咱們在 xhr.responseType 中設置了屬性爲 arraybuffer,那麼在拿到的數據中就能夠用類型化數組來接受啦!
4、小結
本文主要介紹了 Array 在堆棧模型中的存放方式,也詳細描述了 ArrayBuffer 這個原始緩衝區的二進制數據類型,在 web 開發中,數據以及數據的儲存是一個重要的部分,但願引發注意!
5、參考資料