WebSocket系列之JavaScript數字資料如何轉換為二進位制資料

黃Java發表於2018-03-28

概述

本文主要通過對JavaScript中數字資料與二進位制資料之間的轉換,讓讀者能夠了解在JavaScript中如何對數字型別(包括但不限於Number型別)進行處理。

二進位制資料在日常的JavaScript中很少遇到,但是當你使用WebSocket與後端進行資料互動時,就有可能會用到二進位制的資料格式。因此,為了更好的理解本系列中之後釋出的關於WebSocket傳輸二進位制相關的內容,我們有必要了解二進位制資料在JavaScript中是如何進行操作和儲存的。

本文內容主要為:

  • JavaScript中如何操作與儲存二進位制資料——ArrayBuffer儲存結構相關基礎知識以及對應的DataView相關資料型別基礎知識和和API介面,同時對位元組序問題進行介紹。
  • 以Int和Short為例,說明JavaScript中的數字資料如何轉換為二進位制資料。
  • 以Long型別為例,說明JavaScript中如何表示Long型別並且如何將其轉換為二進位制資料。
  • 如何將二進位制資料中轉換為JavaScript中的數字資料。

本文與WebSocket並無太強關聯,不過作為在WebSocket中傳遞二進位制資料的基礎知識儲備,因此放入了此係列當中。

如果讀者對WebSocket並不瞭解,或者說不明白它的使用場景和細節,可以閱讀我的前一篇部落格——WebSocket系列之基礎知識入門篇

如果讀者想了解String型別與二進位制之間的處理和轉換,可以於都WebSocket系列稍後釋出的文章(文章釋出後會替換此段)。

如果讀者想了解在WebSocket中如何進行二進位制的傳遞和解析,可以閱讀WebSocket系列稍後釋出的文章(文章釋出後會替換此段)。

JavaScript中如何儲存和操作二進位制資料

瞭解了為什麼需要使用二進位制資料,我們來看下,在JavaScript中如何儲存和操作二進位制資料。

ArrayBuffer

首先,我們要介紹下在JavaScript中用來儲存二進位制資料的ArrayBuffer

ArrayBuffer 物件用來表示通用的、固定長度的原始二進位制資料緩衝區。

MDN的文件中,我們能夠看到ArrayBuffer的介紹。它是在JavaScript中用來進行二進位制資料儲存的一種資料物件。

下面我們通過一個示例來簡單介紹下ArrayBuffer相關操作。

const buffer = new ArrayBuffer(8);

buffer.byteLength; // 結果為8
複製程式碼

上面的示例通過建立一個長度為8Byte的二進位制資料緩衝區。緩衝區只是一個資料儲存的空間,如何對這個儲存空間進行讀取,完全取決於使用者。例如:8個位元組可以當成是2個Int型別的資料,也可以是一個Long型別的資料,或者4個Short型的資料。

DataView

看完了儲存資料的ArrayBuffer,我們來看下資料讀寫的DataView

DataView 檢視是一個可以從 ArrayBuffer 物件中讀寫多種數值型別的底層介面,在讀寫時不用考慮平臺位元組序問題。

這個是在MDN中關於DataView的介紹。DataView提供了大量的API介面來進行資料的讀和寫操作,我們在後三章將會舉例進行說明。但是,首先我們得先看下說明中提到的位元組序問題。

位元組序

在現有的計算機體系中,有兩種位元組序:

  • 大端位元組序:高位在前,低位在後。符合人類閱讀習慣。
  • 小端位元組序:低位在前,高位在後。符合計算機讀取習慣。

上面所說的順序均是針對多位元組物件而言,如Int型別,Long型別。以Int型別資料0x1234為例,如果是大端位元組序,那麼資料從人類對數值的通常寫法上來看就是0x1234;如果是小端位元組序,那麼從人類對數值的通常寫法上來看,應該寫成0x3412

對於單位元組物件如Byte型別資料而言,沒有位元組序一說。

在不同的平臺中,可能使用不同的位元組序,這就是所謂的位元組序問題。DataView所謂的在讀寫時不需要考慮平臺位元組序問題是指:同時使用DataView進行寫入和讀取的資料保持一致。

JavaScript中的數字資料如何轉換為二進位制資料

對ArrayBuffer和DataView有了一個大概的瞭解,下面讓我們來看下它是如何進行二進位制資料操作的。

本章,我以Short型別和Int型別為例,介紹下相關操作步驟。

let buffer = new ArrayBuffer(6); // 初始化6個Byte的二進位制資料緩衝區
let dataView = new DataView(buffer);

dataView.setInt16(0, 3); // 從第0個Byte位置開始,放置一個數字為3的Short型別資料(佔2 Byte)
dataView.setInt32(2, 15); // 從第2個Byte位置開始,放置一個數字為15的Short型別資料(佔4 Byte)
複製程式碼

通過上面的示例,我們一共初始化了6個Byte的儲存空間,使用1個Short型別(佔2 Byte)和一個Int型別(佔4 Byte)的資料進行填充。

DataView還提供了許多的API介面來進行其他資料型別的處理,如無符號型,浮點數等。他們的使用方法和上面介紹的API相同,我們在這裡就不一一進行介紹了,希望瞭解更多API介面的讀者可以檢視MDN文件

JavaScript中如何表示Long型別並且如何將其轉換為二進位制資料

通過DataView提供的API介面,我們知道了如何處理Short型別、Int型別、Float型別和Double型別。那麼,如果是對於Long型別這種原生API中沒有提供處理函式的資料型別,我們應該如何處理呢?

首先,我們需要理解Long資料型別的結構,它是由一個高位的4個Byte和低位的4個Byte組成的資料型別。因為Long型別表示的範圍比Number型別大,所以我們在JavaScript中是使用了兩個Number型別(即Int型別)的物件來表示Long型別資料,相關的具體細節可以見我之前的部落格Long.js原始碼分析與學習

理解了JavaScript中如何儲存Long型別,我們就知道如果對其進行儲存。

import Long from 'long';

let long = Long.fromString('123');
let buffer = new ArrayBuffer(8);
let dataView = new DataView(buffer);

dataView.setInt32(0, long.high); // 採用大端位元組序放置
dataView.setInt32(4, long.low);
複製程式碼

通過上面的示例,我們將一個Long型別的資料拆分成了兩個Int型別的資料,按照大端位元組序放入到了ArrayBuffer中。同理,如果是想按照小端位元組序放置,只需要將資料進行部分處理後再放入即可,在此我就不過多介紹了。

如何將二進位制資料中轉換為JavaScript中的資料型別

當你知道了如何將資料轉換為ArrayBuffer中儲存的二進位制資料後,就能夠簡單推測出如何進行反向操作——將資料從ArrayBuffer中讀取出來,再轉換成JavaScript中常用資料型別。

import Long from 'long';

let buffer = new ArrayBuffer(14); // 初始化14個Byte的二進位制資料緩衝區
let dataView = new DataView(buffer);
let long = Long.fromString('123');


// 資料寫入過程

dataView.setInt16(0, 3); // 從第0個Byte位置開始,放置一個數字為3的Short型別資料(佔2 Byte)
dataView.setInt32(2, 15); // 從第2個Byte位置開始,放置一個數字為15的Short型別資料(佔4 Byte)

dataView.setInt32(6, long.high); // 採用大端位元組序放置
dataView.setInt32(10, long.low);

// 資料讀取過程

let shortNumber = dataView.getInt16(0);
let intNumber = dataView.getInt32(2);

let longNumber = Long.fromBits(dataView.getInt32(10), dataView.getInt32(6)); // 根據大端位元組序讀取,該建構函式入參依次為:低16位,高16位
複製程式碼

通過上面的示例,我們將一串二進位制資料轉換成為了JavaScript中通用的資料型別。

總結

通過使用ArrayBuffer和DataView,我們能夠快速的將數字資料從二進位制轉換為JavaScript常用資料型別如Int、Short等;同時,我們也可以將這些資料型別轉換為二進位制資料。有了這些基礎知識,我們就能夠理解在之後的部落格中講到的關於使用WebSocket進行二進位制資料傳遞的過程和處理邏輯。

下一篇部落格我們將介紹String型別相關的二進位制處理與轉換操作,有興趣的同學可以關注留意下相關內容。

部分參考資料

阮一峰老師關於位元組序的介紹

相關文章