相信每一個javascript 學習者,都會去了解JS 的各種基本數據類型,數組就是數據的組合,這是一個很基本也十分簡單的概念,他的內容沒多少,學好它也不是件難事情。但是本文著重要介紹的並不是我們往常看到的Array,而是ArrayBuffer。
我寫的很多東西都是因為要完成某些特定的功能而刻意總結的,可以算是備忘,本文也是如此!前段時間一直在研究Web Audio API 以及語音通信相關的知識,內容側重於音頻流在AudioContext 各個節點之間的流動情況,而現在要摸清楚音頻到流底是個什麼樣的數據格式,所以對ArrayBuffer 的研究就顯得格外重要了。
Array 在內存中的堆棧模型
Array 的獲取
Javascript 中如何產生Array:
複製代碼代碼如下:
[element0, element1, ..., elementN]
new Array(element0, element1, ..., elementN)
new Array(arrayLength)
直接定義,或者通過構造函數創建一個Array,當然也可以使用其他的手段:
複製代碼代碼如下:
"array".split("");
"array".match(/a|r/g);
等等,方式有很多。但是Array 內部是個什麼樣的結構,恐怕很多人還不是很清楚。
堆棧模型
在數組中我們可以放很多不同數據類型的數據,如:
複製代碼代碼如下:
var arr = [21, "李靖", new Date(), function(){}, , null];
上面這個數組中一次放入了數字、字符串、對象、函數、undefined 和null,對於上面的數據接口我們可以具象的描述下:
複製代碼代碼如下:
棧
+---------+ 堆
| 21 | +-------------------+
+---------+ | |
| "李靖" | | |
+---------+ | +--------+ |
| [refer] |----------->| Object | |
+---------+ | +--------+ |
| [refer] |----------------->+--------+ |
+---------+ | |function| |
|undefined| | +--------+ |
+---------+ | |
| null | +-------------------+
+---------+ Created By Barret Lee
JavaScript 的數據類型分為兩種,一種是值類型,一種是引用類型,常見的引用類型有Object 和Array,數組的儲存模型中,如果是諸如Number、String 之類的值類型數據會被直接壓入棧中,而引用類型只會壓入對該值的一個索引,用C 語言的概念來解釋就是只保存了數據的指針,這些數據是儲存在堆中的某塊區間中。棧堆並不是獨立的,棧也可以在堆中存放。
好了,對Array 的說明就到這裡,下面具體說說ArrayBuffer 的相關知識。
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 是弱類型語言,但是他本身是對數據的類型和大小都有限制的,我們需要通過某種數據結構將緩衝區的內容有序的讀取出來(寫進去)。
原始緩衝區的創建
通過ArrayBuffer 這個構造函數可以創建一個原始緩衝區:
複製代碼代碼如下:
var buffer = new ArrayBuffer(30);
從chrome 控制台可以看到:
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]);
數據化數組
類型化數組類型表示可編制索引和操縱的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
Created By Barret Lee
看了上面的圖解還有疑問麼?我覺得我不用繼續解釋了。可以把ArrayBuffer 的單位看成1,而Float32Array 的單位是4.
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
如果感興趣,可以移步http://www.javascripture.com/DataView,作詳細了解。
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,那麼在拿到的數據中就可以用數據化數組來接受啦!
小結
本文主要介紹了Array 在堆棧模型中的存放方式,也詳細描述了ArrayBuffer 這個原始緩衝區的二進制數據類型,在web 開發中,數據以及數據的儲存是一個重要的部分,希望引起注意!
本文敘述上可能存在錯誤,請多多斧正!