溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript中怎么對二進(jìn)制進(jìn)行操作

發(fā)布時(shí)間:2021-07-15 11:41:00 來源:億速云 閱讀:217 作者:Leah 欄目:web開發(fā)

這篇文章將為大家詳細(xì)講解有關(guān)JavaScript中怎么對二進(jìn)制進(jìn)行操作,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

二進(jìn)制數(shù)據(jù)在JS程序里的表達(dá)

現(xiàn)今世界上幾乎所有的計(jì)算機(jī)體系結(jié)構(gòu)都是以字節(jié)(byte)為二進(jìn)制數(shù)據(jù)的基本單位(注:不是說最小單位),所以二進(jìn)制常常以字節(jié)數(shù)組的形式存在于程序當(dāng)中。例如在C#里面,就用byte[],標(biāo)準(zhǔn)C里面沒有byte類型,但可以通過typedef把byte定義為unsigned  char的別名,效果是一樣的。

JS設(shè)計(jì)之初似乎根本沒想過要處理二進(jìn)制的東西,加上對類型的極度弱化,對于字節(jié)的概念可以說是非常非常的模糊。如果要表達(dá)字節(jié)數(shù)組,那么似乎只能用一個(gè)普通數(shù)組來表示。

HTML5體系引入了一大堆新的東西,比如XHR2,是可以上傳或下載二進(jìn)制內(nèi)容的,與之配套的東西就是JS里的ArrayBuffer和Typed  Array了。

ArrayBuffer是一個(gè)固定長度的字節(jié)序列,你可以通過new  ArrayBuffer(length)來得到一片空間,或者用下文將會介紹的方法從XHR2等途徑獲取。由于內(nèi)部實(shí)現(xiàn)與數(shù)組不一樣,ArrayBuffer通常都是連續(xù)內(nèi)存(注意,這只是經(jīng)驗(yàn)之談,并不是規(guī)范也不是文檔所明確的),因此對于高密度的訪問操作而言它比JS中的Array速度會快很多(但并不要用它來簡單地代替Array)。如果用Chrome的Profile工具查看Heap  Snapshot,會發(fā)現(xiàn)ArrayBuffer會被單獨(dú)列為一類,也許它的內(nèi)存分配和布局與Array以及其他JS對象有一些差別吧。

ArrayBuffer是不能直接被訪問的,因此需要借助Typed Array。Typed  Array是一組具體數(shù)據(jù)類型的Array-Like類型的統(tǒng)稱,包括

  • Int8Array 8位有符號整數(shù),類似于C里面的char

  • Uint8Array 8位無符號整數(shù),類似于C里面的unsigned char

  • Uint8ClampedArray 8位無符號整數(shù),跟Uint8類似,但在溢出處理上不大一樣

  • Int16Array 后面這些類型就不羅嗦了

  • Uint16Array

  • Int32Array

  • Uint32Array

  • Float32Array

  • Float64Array

Typed Array的背后是一個(gè)ArrayBuffer,也就是說,事實(shí)上的數(shù)據(jù)是存在ArrayBuffer里面的,而Typed  Array只是給你提供了一個(gè)某種類型的讀寫接口,用MDN的話說,叫做

Multiple views on the same data

舉個(gè)栗子,如果我們有一個(gè)ArrayBuffer名為buffer(先不考慮怎么構(gòu)造這個(gè)測試數(shù)據(jù)),內(nèi)容如下:

01 02 03 04 05 06 07 08

也就是說它有8個(gè)字節(jié),我們分別用它來構(gòu)造Uint8Array, Uint16Array, Uint32Array,則可以得到

var u8 = new Uint8Array(buffer); // length為8  var u16 = new Uint16Array(buffer); // length為4  var u32 = new Uint32Array(buffer); // length為2

它們的內(nèi)容分別為

[1, 2, 3, 4, 5, 6, 7, 8]  [513, 1027, 1541, 2055]  [67305985, 134678021]

這不難理解。

可以看出,如果要手工構(gòu)造上面的測試數(shù)據(jù)ArrayBuffer,用Uint8Array就會很方便(呃事實(shí)上這是我個(gè)人最常用的一種Typed  Array)。

而如果用同樣的ArrayBuffer構(gòu)建帶符號整數(shù)類型,則可能因?yàn)檎麛?shù)溢出而得到不同的結(jié)果,上面的例子并沒有碰到,有興趣的話可以自己試試。因此使用Typed  Array也可以用來做有符號數(shù)和無符號數(shù)的轉(zhuǎn)換。

如果你用過canvas的getImageData/putImageData的話,會發(fā)現(xiàn)它給你的就是一個(gè)Uint8ClampedArray,這東西訪問起來速度比JS的原生Array快很多,使得對canvas進(jìn)行高速的像素操作成為可能。

然而最最重要的一個(gè)概念還是:Typed Array不直接存放任何數(shù)據(jù),所有對Typed  Array進(jìn)行讀寫的操作,最終都會落實(shí)到它背后所持有的ArrayBuffer的身上。ArrayBuffer才是真正的raw bytes,而Typed  Array只是一個(gè)操作窗口/操作視圖(View)。

獲取二進(jìn)制數(shù)據(jù)

nodejs那邊先按住不表,這里談?wù)勗诰W(wǎng)頁里如何獲取二進(jìn)制數(shù)據(jù)?常見的辦法有3種,1是通過XMLHttpRequest  2,2是通過File和Blob一套相關(guān)接口。

通過XMLHttpRequest 2

XHR2的接口跟XHR幾乎是一樣的,當(dāng)制定xhr.responseType =  'arraybuffer'以后,在成功獲取數(shù)據(jù)的回調(diào)里就可以通過xhr.response來得到請求結(jié)果的ArrayBuffer了,然后就可以按照你的意愿來構(gòu)造各種Typed  Array進(jìn)行訪問。

responseType還可以有blob取值,可以用xhr.response獲得Blob對象。

通過File和Blob

在HTML5中提供了對表單的文件控件<input type="file"  />更豐富的操作,可以通過inputDOM對象的.files來獲取一個(gè)FileList,當(dāng)然通常瀏覽器都只提供了單選的文件控件,于是這里都只會有一個(gè)File對象。另外,通過拖拽、剪貼板等方式也能獲取到File或者Blob。

File繼承了Blob,并提供了name, lastModifiedDate等基礎(chǔ)元數(shù)據(jù),但是依然是一個(gè)深度封裝,不能直接獲取到它的二進(jìn)制。

Blob是Binary large object的縮寫,它與ArrayBuffer的區(qū)別是除了raw bytes以外它還提供了mime  type作為元數(shù)據(jù)。但它依然是無法直接被讀寫的。

這時(shí)候需要借助FileReader的幫忙。FileReader提供了一組用來將Blob讀取為更為實(shí)用的類型的方法

readAsArrayBuffer()  readAsBinaryString()  readAsDataURL()  readAsText()

例如

var file = get_file_some_how();  var fr = new FileReader();  fr.onload = function(e) {  e.target.result; // 讀取的結(jié)果  };  fr.readAsDataUrl(file); // readAsArrayBuffer

可以干什么呢?例如圖片上傳之前的本地預(yù)覽(甚至基于canvas的編輯)等等都可以實(shí)現(xiàn)了。

Blob的其他構(gòu)造方法多而雜,這里就先不到處搬運(yùn)文檔了。

消費(fèi)二進(jìn)制數(shù)據(jù)

何謂消費(fèi)?最常見的方式也許就是通過XHR2直接把二進(jìn)制數(shù)據(jù)以文件方式POST到服務(wù)端去。

這里我比較推薦使用FormData來構(gòu)造POST數(shù)據(jù)。因?yàn)樵诜?wù)端收的時(shí)候會比較容易一些,具體有興趣可以去找找別人的例子。

雖然直接提交ArrayBuffer也是可以的,但是這種時(shí)候服務(wù)端收到的POST  body會是一大團(tuán),用起來不方便。如果要使用FormData來提交ArrayBuffer,需要先將其構(gòu)造成Blob。

對Typed Array的構(gòu)造留個(gè)心眼

當(dāng)使用new xxxxxArray(arrayBuffer)這個(gè)重載進(jìn)行構(gòu)造的時(shí)候,它會默認(rèn)基于此ArrayBuffer進(jìn)行構(gòu)造。但當(dāng)使用new  xxxxArray(another_typed_array)這個(gè)重載的時(shí)候,則是進(jìn)行“拷貝構(gòu)造”,這樣兩個(gè)Typed  Array會指向不同的buffer,需要注意這是否符合預(yù)期。

如果需要基于同一個(gè)ArrayBuffer來構(gòu)造Typed Array,可以使用Typed Array的buffer,  byteLength,byteOffset來獲取它背后的ArrayBuffer。

Tips(坑)

對內(nèi)存對齊留個(gè)心眼

當(dāng)使用ArrayBuffer來構(gòu)造Typed Array的時(shí)候,可以指定byteOffset參數(shù),例如

var buffer = get_array_buffer_some_how();  var i16 = new Int16Array(buffer, 10);

上面的代碼就能以buffer向后偏移10字節(jié)處為起點(diǎn)來構(gòu)造Int16Array,但是如果將10設(shè)置為一個(gè)奇數(shù),會發(fā)現(xiàn)如下錯誤:

RangeError: start offset of Int16Array should be a multiple of 2

這是因?yàn)門yped  Array對內(nèi)存對齊有要求,它不能在非對齊的位置建立,同理,Uint32Array和Int32Array則要求偏移量是4字節(jié)對齊的。

因此如果你希望在非對齊的位置進(jìn)行讀寫,則需要借助DataView的幫忙。

對字節(jié)序留個(gè)心眼

我們?nèi)粘V兴鶎懙某绦颍瑤缀醵疾恍枰P(guān)心字節(jié)序,因此這個(gè)問題沒那么嚴(yán)重,知道自己的程序會有字節(jié)序問題的人,開發(fā)到這里也肯定會知道問題的存在,但這里還是稍微提一下。

按照MDN的說法,Typed  Array只會使用當(dāng)前平臺的字節(jié)序,例如我們現(xiàn)在用的桌面電腦不論P(yáng)C還是Mac都是x86/x64的,也就是little-endian了。

使用DataView,不僅可以解決上面說到的內(nèi)存對齊的問題,還可以指定讀寫時(shí)的字節(jié)序,具體參數(shù)都在文檔里面了,就不搬運(yùn)了。

使用DataView配合Typed Array也可以做到一個(gè)檢測當(dāng)前平臺字節(jié)序的技巧:

function isLittleEndian() {  var buf = new ArrayBuffer(2);  var view = new DataView(buf);  view.setInt16(0, 256, true);//顯式以little endian寫入數(shù)據(jù)  // 此時(shí)buf里的內(nèi)存布局應(yīng)該是 00 01   var i16 = new Int16Array(buf);  // 如果以little endian讀取,它就是256;以big endian讀取,則是1  return (i16[0] === 256);  }

關(guān)于JavaScript中怎么對二進(jìn)制進(jìn)行操作就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI