這個部分若是沒有C語言和計算機基礎會比較難理解,若是實在理解不了能夠收藏它,往後再看。數組
二進制數組其實很早就有了,不過爲了 WebGL 中,數據能夠高效和顯卡交換數據。分爲3類:app
數據類型 | 字節長度 | 含義 | 對應 C 語言類型 | TypedArray 類型 | DataView 類型 |
---|---|---|---|---|---|
Int8 | 1 | 8位有符號整數 | char | Int8Array | Int8 |
Uint8 | 1 | 8位無符號整數 | unsigned char | Uint8Array | Uint8 |
Uint8C | 1 | 8位無符號整數(自動過濾溢出) | unsigned char | Uint8ClampedArray | 不支持 |
Int16 | 2 | 16位有符號整數 | short | Int16Array | Int16 |
Uint16 | 2 | 16位無符號整數 | unsigned short | Uint16Array | Uint16 |
Int32 | 4 | 32位有符號整數 | int | Int32Array | Int32 |
Uint32 | 4 | 32位無符號整數 | unsigned int | Uint32Array | Uint32 |
Float32 | 4 | 32位浮點數 | float | Float32Array | Float32 |
Float64 | 8 | 64位浮點數 | double | Float64Array | Float64 |
ArrayBuffer 表明內存中的一段二進制數據,咱們無法直接操做,須要利用視圖(TypedArray,DataView)按必定格式解讀二進制數據。但咱們依然能夠構造一段內存來存放二進制數據:函數
var buf = new ArrayBuffer(32); //分配32個字節的內存存放數據, 默認全0 var dataview = new DataView(buf); //將這段內存轉爲視圖 dataview.getUint8(0); //獲得第一個8字節的值(無符號),0
這裏須要強調的是,分配內存空間不要太大!畢竟你的內存是有限的。
其次,不管使用什麼視圖,其實例化的內存若是共享,全部的寫入操做會修改每個視圖,由於內存共用的:this
var buf = new ArrayBuffer(32); var view16 = new Int16Array(buf); var viewu8 = new Uint8Array(buf); console.log(viewu8[0]); //0 view16[0]=-1; console.log(viewu8[0]); //255
這裏之因此獲得255,是由於內存共用致使的,但爲什麼不是-1?Int16Array 是有符號類型的,這樣二進制的最高位用做符號位,負數記爲1:1000 0000 0000 0001
,以後的數字用移碼存儲,獲得-1的二進制爲:1111 1111 1111 1111
, 以後利用Uint8Array讀取無符號的前8位,獲得1111 1111
這個計算爲十進制爲 $2^8-1=255$。具體關於數制轉換和反碼補碼這裏再也不展開,不然就跑偏了。編碼
ArrayBuffer 對象也有幾個方法和屬性:code
const N = 32; var buf = new ArrayBuffer(N); if(buf.byteLength === N){ //分配成功 } else { //分配失敗 }
var buf = new ArrayBuffer(32); var newBuf = buf.slice(0,3);
var buf1 = new ArrayBuffer(32); var buf2 = new ArrayBuffer(32); var buf1View = new Int8Array(buf1); var buf2View = new Int8Array(buf2); buf1.isView(buf1View); //true buf1.isView(buf2View); //false
具備一個構造函數 DataView(), 接受一個ArrayBuffer參數,視圖化該段內存;或接受一個數組參數,實例化該數組爲二進制內容。獲得的值是一個數組,能夠直接使用[]
訪問每一個位置的內容,有length
屬性。其構造函數有9個:orm
數據類型 | 字節長度 | 含義 | 對應 C 語言類型 | TypedArray 類型構造函數 |
---|---|---|---|---|
Int8 | 1 | 8位有符號整數 | char | Int8Array() |
Uint8 | 1 | 8位無符號整數 | unsigned char | Uint8Array() |
Uint8C | 1 | 8位無符號整數(自動過濾溢出) | unsigned char | Uint8ClampedArray() |
Int16 | 2 | 16位有符號整數 | short | Int16Array() |
Uint16 | 2 | 16位無符號整數 | unsigned short | Uint16Array() |
Int32 | 4 | 32位有符號整數 | int | Int32Array() |
Uint32 | 4 | 32位無符號整數 | unsigned int | Uint32Array() |
Float32 | 4 | 32位浮點數 | float | Float32Array() |
Float64 | 8 | 64位浮點數 | double | Float64Array() |
以上9個會對內存進行不一樣位數的格式化,以獲得對應類型值的數組。這個數組不一樣於普通數組,它不支持稀疏數組,默認值爲0,並且同一個數組只能存放同一個類型的變量。對象
以上每一個構造函數都對應以下形式的參數:內存
(buffer, start=0, len=buffer.byteLength-start*8)
能夠指定序列化其中 start到 end部分的二進制數據。注意這裏指定的範圍必須和數組類型所匹配,不能出現相似new Int32Array(buffer,2,2)
的狀況。若是你以爲這個不符合你的需求,可使用 DataView。字符串
若是你以爲上面的寫法複雜,能夠不寫 new ArrayBuffer,直接使用 TypedArray,但注意參數的意義不同:
var f64a = new Float64Array(4); //分配32個字節,並做爲double類型使用。 32 = 64 / 8 * 4
TypedArray的構造函數還接受另外一個TypedArray做爲參數,開闢新內存複製其值並改變類型,對原視圖和buffer 不構成影響,也不共用內存。
TypeArray的構造函數還接受另外一個Array做爲參數,開闢新內存複製其值,對原數組不構成影響,也不共用內存。
固然利用一下方法,能夠把 TypedArray 轉換爲普通數組:
var arr = [].slice.call(typedArray);
TypedArray具備除了concat()
之外的所有數組方法,固然,它也具備 iterator,能夠用 for...of 遍歷。
如下是 TypedArray 特有的屬性和方法:
小技巧,轉換字符串和 ArrayBuffer
//該方法僅限轉換 utf-16 的字符串 function ab2str(buf){ return String.fromCharCode.apply(null, new Uint16Array(buf)); } function str2ab(str){ var len = str.length; var view = new Uint16Array(len); for(let i = 0; i < len; i++){ view[i] = str.charCodeAt(i); } return view.buffer; } var str = "Hello world"; var buf = str2ab(str); var view = new Uint16Array(buf); for(var i = 0; i < view.length; i++){ console.log(String.fromCharCode(view[i])); //一次輸出"Hello world"的每一個字母 } console.log(ab2str(buf)); //"Hello world"
這裏擴展一些編碼知識,咱們知道計算機裏面存儲的是二進制,而且存儲的最小單位是字節。可是不一樣的系統存儲方式不一樣,分爲高位優先和低位優先。好比 20170101 這個數字,其十六進制表示爲 0x0133C575, 在低位優先的系統中存儲方式爲0x75 0xC5 0x33 0x01
, 而在高位優先的系統中存儲方式爲0x01 0x33 0xC5 0x75
。因爲大多數計算機採用低位優先的方式,因此 ES6 採用是也是低位優先的方式,但遇到高位優先的數據時,就不能簡單的直接那來使用,具體使用會在 DataView 中介紹,這裏說明一種判斷低位優先(little endian)仍是高位優先(big endian)的方法:
還有須要注意的是數據溢出,這個也是須要數制方面基礎比較好理解,這裏不過多展開了。舉一個例子:
Uint8 只能表示8位無符號整數,最大是1111 1111
, 也就是十進制的 0~255;Int8由於有了符號位,只能表示十進制-128~127,若是給它的值不在這個範圍內就會發生溢出,獲得一個你意想不到但情理之中的值
var view1 = new Uint8Array(2); view1[0] = 256; //256 二進制是 1 0000 0000 因爲數據只能容納8個值,進位1就丟了 view1[1] = -1; //以前說過-1 二進制(補碼)爲 1111 1111(全1), 做爲無符號數8個1就是255 console.log(view1[0]); //0 console.log(view1[1]); //255 var view2 = new Int8Array(2); view2[0] = 128; //因爲符號位溢出,系統自動用32位計算這個數1 000 0000 0000 0000 0000 0000 1000 0000,取符號位和最後8位獲得-128 view2[1] = -128; //因爲符號位溢出,系統自動用32位計算這個數0 111 1111 1111 1111 1111 1111 0111 1111,取符號位和最後8位獲得127 console.log(view2[0]); //-128 console.log(view2[1]); //127
爲了防止這樣的狀況,js 有一個 Unit8ClampedArray, 使整數方向的溢出值爲255,0方向的易楚志爲0。注意這是個無符號的類型;
var view = new Uint8ClampedArray(2); view[0] = 256; view[1] = -1; console.log(view[0]); //255 console.log(view[1]); //0
劃分一塊 buffer 使用獲得 C 語言中的結構體
var buf = new ArrayBuffer(24); var name = new Uint8Array(buf, 0, 16); var gender = new Uint8Array(buf, 16, 1); var age = new Uint16Array(buf, 18, 1); var score = new Float32Array(buf,20,1);
至關於如下 C語言代碼
struct Person{ char name[16]; char gender; int age; float score; }
共用一塊 buffer 使用獲得 C 語言中的聯合體
var buf = new ArrayBuffer(8); var num = new Uint16Array(buf); var dotNum = new Float64Array(buf);
至關於如下 C語言代碼
union Example{ int num[4]; double dotNum; }
具備一個構造函數 DataView(), 接受一個ArrayBuffer參數,視圖化該段內存。畢竟當一段內存有多種數據時,複合視圖也不是那麼方便,這時適合使用 DataView 視圖。其次 DataView 能夠自定義高位優先和低位優先,這樣能夠讀取的數據就更多了。
DataView構造函數形式以下,這一點和 TypedArray 一致:
(buffer, start=0, len=buffer.byteLength-start*8)
它具備如下方法格式化讀取 buffer 中的信息:
它具備如下方法格式化寫入 buffer 中的信息:
它具備如下屬性和方法:
若是你不知道計算機使用的是高位優先仍是低位優先,也能夠自行判斷:
//方法1 const BIG_ENDIAN = Symbol('BIG_ENDIAN'); const LITTLE_ENDIAN = Symbol('LITTLE_ENDIAN'); function getPlatformEndianness(){ let arr32 = Uint32Array.of(0x12345678); let arr8 = new Uint8Array(arr32.buffer); switch((arr8[0]*0x1000000)+(arr8[1]*0x10000)+(arr8[2]*0x100)+arr8[3]){ case 0x12345678: return BIG_ENDIAN; case 0x78563412: return LITTLE_ENDIAN; default: throw new Error("unknow Endianness"); } } //方法2 window.isLittleEndian = (function(){ var buffer = new ArrayBuffer(2); new DataView(buffer).setInt16(0, 256, true); return new Int16Array(buffer)[0] === 256; }());