1. 程式人生 > >ES6學習筆記---二進位制陣列(概念)

ES6學習筆記---二進位制陣列(概念)

來源:https://segmentfault.com/a/1190000003868111

二進位制陣列由三類物件組成:

  • ArrayBuffer物件:代表原始的二進位制資料。

  • TypedArray檢視:用來讀寫簡單型別的二進位制資料。

  • DataView檢視:用來讀寫複雜型別的二進位制資料。

下面詳細瞭解一下它們的用法:

1. ArrayBuffer 物件

它不能直接讀寫,只能通過(TypedArrayDataView)來讀寫。

ArrayBuffer也是一個建構函式,可以分配一段可以存放資料的連續記憶體區域。

例如我想生成一段32位元組的記憶體區域,每個位元組的值預設都是0,就可以這樣建立:

var buf = new ArrayBuffer(32);

ArrayBuffer.prototype.byteLength

byteLength:是ArrayBuffer例項的屬性,返回所分配的記憶體區域的位元組長度。

var buffer = new ArrayBuffer(32);
buffer.byteLength     // 32

需要注意的是:如果你想分配一塊比較大的記憶體區域,需要檢查是否分配成功,因為可能沒有那麼多的連續空餘記憶體。

if(buffer.byteLength === n){
    //成功
}else{
    //失敗
}

ArrayBuffer.prototype.slice()

slice:有兩個引數,第一個引數表示開始拷貝的位元組序號(包含此位元組),第二個引數表示截止拷貝的位元組序號(不含此位元組),如果省略第二個引數,則預設截止到原ArrayBuffer物件的結尾。

slice:是ArrayBuffer例項的一個方法,允許將記憶體區域的一部分,拷貝生成一個新的ArrayBuffer物件。

var buffer = new ArrayBuffer(8);
var newBuffer = buffer.slice(0, 3);
//拷貝`buffer`物件的前三個位元組(從0到3前結束),生成一個新的ArrayBuffer物件:newBuffer

ArrayBuffer.isView()

isView:是ArrayBuffer的一個靜態方法,返回一個布林值,表示引數是否為ArrayBuffer的檢視例項。這個方法大致相當於判斷引數,是否為TypedArray例項或DataView例項。

var buffer = new ArrayBuffer(8);
ArrayBuffer.isView(buffer) // false

var v = new Int32Array(buffer);
ArrayBuffer.isView(v) // true

2. TypedArray 檢視

TypedArray陣列只提供9種固定的建構函式,用來生成相應型別的陣列例項。

(1)TypedArray(buffer, byteOffset=0, length?)

檢視的建構函式可以接受三個引數:

  • 第一個引數(必需):檢視對應的底層ArrayBuffer物件。

  • 第二個引數(可選):檢視開始的位元組序號,預設從0開始。

  • 第三個引數(可選):檢視包含的資料個數,預設直到本段記憶體區域結束。

// 建立一個8位元組的ArrayBuffer
var b = new ArrayBuffer(8);

// 建立一個指向b的Int32檢視,開始於位元組0,直到緩衝區的末尾
var v1 = new Int32Array(b);

// 建立一個指向b的Uint8檢視,開始於位元組2,直到緩衝區的末尾
var v2 = new Uint8Array(b, 2);

// 建立一個指向b的Int16檢視,開始於位元組2,長度為2
var v3 = new Int16Array(b, 2, 2);

v1[0]是一個32位整數,指向位元組0~位元組3;v2[0]是一個8位無符號整數,指向位元組2;v3[0]是一個16位整數,指向位元組2~位元組3。只要任何一個檢視對記憶體有所修改,就會在另外兩個檢視上反應出來。

注:byteOffset必須與所要建立的資料型別一致,比如:帶符號的16位整數需要兩個位元組,所以byteOffset引數必須能夠被2整除

(2)TypedArray(length)

檢視還可以不通過ArrayBuffer物件,直接分配記憶體而生成。

var f64a = new Float64Array(8); //64位元組
f64a[0] = 10;
f64a[1] = 20;
f64a[2] = f64a[0] + f64a[1]; // 30

(3)TypedArray(typedArray)

TypedArray陣列的建構函式,可以接受另一個TypedArray例項作為引數。

var typedArray = new Int8Array(new Uint8Array(4));

注:新陣列會開闢一段新的記憶體存資料。

var x = new Int8Array([1, 1]);
var y = new Int8Array(x);
x[0] // 1
y[0] // 1

x[0] = 2;
y[0] // 1

//基於同一段記憶體
var x = new Int8Array([1, 1]);
var y = new Int8Array(x.buffer);
x[0] // 1
y[0] // 1

x[0] = 2;
y[0] // 2

(4)TypedArray(arrayLikeObject)

//普通陣列生成TypedArray例項
var typedArray = new Uint8Array([1, 2, 3, 4]);
//這時TypedArray檢視會重新開闢記憶體,不會在原陣列的記憶體上建立檢視


//TypedArray陣列轉換回普通陣列
var normalArray = Array.prototype.slice.call(typedArray);

需要注意的是:TypedArray陣列除了沒有concat方法之外,擁有普通陣列所有的操作方法和屬性

如果想要合併多個TypedArray陣列,可以用下面這個函式。

function concatenate(resultConstructor, ...arrays) {
  let totalLength = 0;
  for (let arr of arrays) {
    totalLength += arr.length;
  }
  let result = new resultConstructor(totalLength);
  let offset = 0;
  for (let arr of arrays) {
    result.set(arr, offset);
    offset += arr.length;
  }
  return result;
}

concatenate(Uint8Array, Uint8Array.of(1, 2), Uint8Array.of(3, 4))
// Uint8Array [1, 2, 3, 4]

TypedArray陣列也可以被遍歷。

let ui8 = Uint8Array.of(0, 1, 2);
for (let byte of ui8) {
  console.log(byte);
}
// 0
// 1
// 2

ArrayBuffer 與字串的互相轉換

注:字串的編碼方法是確定的,才可以相互轉換。

// ArrayBuffer轉為字串,引數為ArrayBuffer物件
function ab2str(buf) {
  return String.fromCharCode.apply(null, new Uint16Array(buf));
}

// 字串轉為ArrayBuffer物件,引數為字串
function str2ab(str) {
  var buf = new ArrayBuffer(str.length * 2); // 每個字元佔用2個位元組
  var bufView = new Uint16Array(buf);
  for (var i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}

TypedArray.prototype.buffer:返回整段記憶體區域對應的ArrayBuffer物件,屬性為只讀。

TypedArray.prototype.byteLength:返回TypedArray陣列佔據的記憶體長度,單位為位元組,屬性為只讀。

TypedArray.prototype.byteOffset:返回TypedArray陣列從底層ArrayBuffer物件的哪個位元組開始,屬性為只讀。

TypedArray.prototype.length:TypedArray陣列含有多少個成員。

TypedArray.prototype.set():用於複製陣列(普通陣列或TypedArray陣列)。

TypedArray.prototype.subarray():對於TypedArray陣列的一部分,再建立一個新的檢視。

TypedArray.prototype.slice():返回一個指定位置的新的TypedArray例項。

TypedArray.of():用於將引數轉為一個TypedArray例項。

TypedArray.from():返回一個基於這個結構的TypedArray例項。

3. 複合檢視

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);

上面程式碼將一個24位元組長度的ArrayBuffer物件,分成三個部分:

  • 位元組0到位元組3:1個32位無符號整數

  • 位元組4到位元組19:16個8位整數

  • 位元組20到位元組23:1個32位浮點數

4. DataView檢視

DataView例項有以下屬性,含義與TypedArray例項的同名方法相同。

  • DataView.prototype.buffer:返回對應的ArrayBuffer物件

  • DataView.prototype.byteLength:返回佔據的記憶體位元組長度

  • DataView.prototype.byteOffset:返回當前檢視從對應的ArrayBuffer物件的哪個位元組開始

DataView例項提供8個方法讀取記憶體。

  • getInt8:讀取1個位元組,返回一個8位整數。

  • getUint8:讀取1個位元組,返回一個無符號的8位整數。

  • getInt16:讀取2個位元組,返回一個16位整數。

  • getUint16:讀取2個位元組,返回一個無符號的16位整數。

  • getInt32:讀取4個位元組,返回一個32位整數。

  • getUint32:讀取4個位元組,返回一個無符號的32位整數。

  • getFloat32:讀取4個位元組,返回一個32位浮點數。

  • getFloat64:讀取8個位元組,返回一個64位浮點數。

DataView檢視提供8個方法寫入記憶體。

  • setInt8:寫入1個位元組的8位整數。

  • setUint8:寫入1個位元組的8位無符號整數。

  • setInt16:寫入2個位元組的16位整數。

  • setUint16:寫入2個位元組的16位無符號整數。

  • setInt32:寫入4個位元組的32位整數。

  • setUint32:寫入4個位元組的32位無符號整數。

  • setFloat32:寫入4個位元組的32位浮點數。

  • setFloat64:寫入8個位元組的64位浮點數。