淺析ArrayBuffer、TypedArray和Buffer

前言

接觸node後,少不了會對文件進行操做,因而你可能會遇到如下問題:html

  • 在寫請求頭時,有一個叫做返回響應數據的類型的屬性(responseType),支持一個值"arrayBuffer",那arrayBuffer是什麼呢?
  • 在node中處理文件時,常常遇到buffer。好比使用fs.readFile()去讀文件時,第一個參數的類型是能夠是Buffer,那Buffer是什麼呢?
  • 在node的介紹Buffer的官方文檔有提到一個很重要的東西TypedArray,那TypedArray又是什麼呢?

若是你對這三個問題的答案瞭然於心,那麼接下來的文章能夠不用看了。若是有疑問的話,能夠往下看,說不定就能幫你解決疑問。java

二進制數組

首先你須要瞭解一下二進制數組,這對於咱們搞明白上面三個問題很是重要。node

是什麼:用於處理二進制數據的類。es6

爲何存在:javaScript與顯卡通訊的時候,大量的實時的數據交互,用文本格式須要進行格式轉化,二進制則省去轉化時間。api

和數組的關係:buffer的實例相似與整數數組,可是buffer的大小是固定不變的。數組

二進制數組由三類對象組成:網絡

  1. ArrayBuffer對象:表明內存之中的一段二進制數據,自己不能直接操做內存,須要經過「視圖」進行操做。「視圖」部署了數組接口,這意味着,能夠用數組的方法操做內存。
  2. TypedArray視圖:共包括 9 種類型的視圖,好比Uint8Array(無符號 8 位整數)數組視圖, Int16Array(16 位整數)數組視圖, Float32Array(32 位浮點數)數組視圖等等。
  3. DataView視圖:能夠自定義複合格式的視圖,好比第一個字節是 Uint8(無符號 8 位整數)、第2、三個字節是 Int16(16 位整數)、第四個字節開始是 Float32(32 位浮點數)等等,此外還能夠自定義字節序。

一波硬解答:函數

  • ArrayBuffer對象表明原始的二進制數據
  • TypedArray視圖用來讀寫簡單類型的二進制數據(ArrayBuffer),DataView視圖用來讀寫複雜類型的二進制數據(ArrayBuffer)。
  • Node中的Buffer類是以更優化和更適合Nodejs的方式實現了Unit8Array API,意思就是Buffer類實際上是TypedArray(Unit8Array)的nodejs實現。

至此,上面三個問題應該都解決了。想了解更多的能夠繼續往下看。優化

ArrayBuffer

ArrayBuffer對象表明儲存二進制數據的一段內存,它不能直接讀寫,只能經過視圖進行操做。ui

ArrayBuffer也是一個構造函數,能夠分配一段能夠存放數據的連續內存

const buffer = new BufferAarray(12); // 生成一個能夠12個字節的連續內存,每一個字節的默認值是0
複製代碼

以DataView視圖方式讀取

const buffer = new BufferAarray(12);
const dataView = new DataView(buffer); // 對一段12字節的內存創建DataView視圖
dataView.getUnit8(0); // 0 以Unit8方式讀取第一個字節
複製代碼

以TypedArray視圖方式讀取,和DataView不一樣的是DataView是一個構造函數,TypedArray則是一組構造函數

const buffer = new BufferAarray(12);
const x1 = new Unit8Array(buffer); // 創建Unit8Array視圖
const x2 = new Int32Array(buffer); // 創建Int32Array視圖
x1[0] = 1;
x2[0] = 2;
// 因爲兩個視圖是對應的是同一段內存,因此其中一個視圖更改了內存,會影響到另外一個視圖
x1[0]; // 2

複製代碼

ArrayBuffer對象還擁有byteLength屬性和slice方法(此方法是ArrayBuffer對象上惟一能夠讀寫內存的方法)。

ArrayBuffer有一個靜態方法isView,判斷參數是否爲視圖實例。

const buffer = new ArrayBuffer(12);
buffer.byteLength; // 12
buffer.slice(0, 3); // 用法和數組一致,拷貝buffer的前三個字節生成一個新的ArrayBuffer對象
ArrayBuffer.isView(buffer); // false
const dataView = new DataView(buffer);
ArrayBuffer.isView(dataView); // true
複製代碼
複合視圖

因爲視圖的構造函數能夠指定起始位置和長度,因此在同一段內存之中,能夠依次存放不一樣類型的數據,這叫作「複合視圖」。

const buffer = new ArrayBuffer(12);
const a = new Unit8Array(buffer, 0, 1); // 以Unit8Array讀取第一個字節
a[0] = 1;
const b = new Int32Array(buffer, 1, 2); // 以Int32Array讀取第二個字節
b[0] = 2;
複製代碼

視圖

視圖是什麼:ArrayBuffer對象能夠存儲多種類型的數據。不一樣類型的數據有不一樣的解讀方式,這就叫視圖

視圖的做用:以指定格式解讀二進制數據

TypedArray

TypedArray一共包含九種類型,每一種都是一個構造函數。(DataView視圖支持除Unit8ClampedArray之外的八種)

名稱 佔用字節 描述
Int8Array 1 8位有符號整數
Uint8Array 1 8位無符號整數
Uint8ClampedArray 1 8位無符號整型固定數組(數值在0~255之間)
Int16Array 2 16位有符號整數
Uint16Array 2 16位無符號整數
Int32Array 4 32 位有符號整數
Uint32Array 4 32 位無符號整數
Float32Array 4 32 位 IEEE 浮點數
Float64Array 8 64 位 IEEE 浮點數
可接受的參數
  • 視圖的構造函數接受三個參數,第一個ArrayBuffer對象,第二個視圖開始的字節號(默認0),第三個視圖結束的字節號(默認直到本段內存區域結束)
  • 接受ArrayBuffer實例做爲參數,以指定格式讀出二進制數據
  • 可接受普通數組做爲參數,直接分配內存生成ArrayBuffer實例,並同時對這段內存進行賦值,再根據這段內存生成視圖。
  • 可接受視圖作爲參數,生成的新數組複製了參數視圖的值,生成新的數組和視圖。(想基於同一內存生成新視圖,須要傳入視圖.buffer)
和數組的區別
  • TypedArray內的成員只能是同一類型
  • TypedArray成員是連續的,不會有空位
  • TypedArray成員的默認值爲0,數組的默認值爲空
  • TypedArray只是視圖,自己不存儲數據,數據都存儲在底層的ArrayBuffer中,要獲取底層對象必須使用buffer屬性
  • TypedArray能夠直接操做內存,不須要進行類型轉換,因此比數組快
  • TypedArray數組有的方法均可以使用,但不能使用cancat方法

DataView

若是一段數據包含多種類型的數據,除了使用複合視圖的方式讀取以外,還可使用DataView視圖讀取。

DataView視圖提供更多操做選項,並且支持設定字節序。原本,在設計目的上,ArrayBuffer對象的各類TypedArray視圖,是用來向網卡、聲卡之類的本機設備傳送數據,因此使用本機的字節序就能夠了;而DataView視圖的設計目的,是用來處理網絡設備傳來的數據,因此大端字節序或小端字節序是能夠自行設定的。

大端字節序和小端字節序,x68系統使用小端字節序,123456中12比較重要,因此排在後面,存儲順序是563412。大端則相反

  • 一系列get方法用來讀取內存

    const buffer = new ArrayBuffer(24);
    const dv = new DataView(buffer);
    // 接受一個參數
    const v1 = dv.getUnit8(0); // 從第一個字節讀取一個8位無符號整數
    // 讀兩個字節以上時,須要明確數據的存儲模式(true:小端/false:大端)
    const v1 = dv.getUint16(1, true); // 從第二個字節開始,讀取一個16位無符號整數(長度兩個字節)
    複製代碼
  • 一些列set方法用來寫入內存

    // 接受三個參數,1.字節序號,2.寫入的數據,3.寫入方式(true:小端/false|undeifined:大端)
    dv.setInt32(0, 25, false); // 在第一個字節以大端字節序寫入一個值爲25的32位整數
    複製代碼

Node中的Buffer

在引入TypedArray以前,js沒有讀取或操做二進制數據流的機制。Buffer類是做爲nodejs API的一部分引入的,用於在TCP流,文件操做系統,以及上下文中與八位字節流進行交互。

如今可使用 TypedArrayBuffer 類以更優化和更適合 Node.js 的方式實現了 Uint8Array API。

buffer類的實例相似於從0到255之間的整數數組(其餘整數會經過& 255操做強制轉換到範圍內)

buffer.from()

  • buffer.from(array),返回包含給定八位字節數組的副本的新buffer
  • buffer.from(buffer),返回包含給定buffer副本的新buffer
  • buffer.from(arrayBuffer[, betyOffset[, len]]),返回與給定arrayBuffer共享同一段內存的新Buffer
  • buffer.from(string),返回包含給定字符串副本的新Buffer
  • buffer.alloc(size),返回一個指定大小的新建的已經初始化的Buffer
  • buffer.allocUnsafe(size)/buffer.allocUnsafeslow(size),返回一個指定大小的新建的未初始化(沒有被清零)的buffer

初始化較慢,可是能保證新建的buffer實例不包含敏感數據

若是size小於或等於buffer.poolsize的一半,則allocUnsafe生成的buffer實例多是從共享的內部內存池分配,allocUnsafeslow則歷來不使用共享的內部內存池

buffer與typedArray

  • Buffer 實例也是 Uint8Array 實例,可是與 TypedArray 有微小的不一樣。(buffer能夠類比爲視圖)

  • Buffer.from()TypedArray.from() 有着不一樣的實現。具體來講,typedArray.form()可接受第二個參數爲映射函數,buffer.form()不行

參考文檔:

ECMAScript入門--ArrayBuffer

Node.js中文網--Buffer

相關文章
相關標籤/搜索