深入理解JavaScript字節(jié)二進(jìn)制知識以及相關(guān)API
當(dāng)前,前端對二進(jìn)制數(shù)據(jù)有許多的API可以使用,這豐富了前端對文件數(shù)據(jù)的處理能力,有了這些能力,就能夠?qū)D片等文件的數(shù)據(jù)進(jìn)行各種處理。
本文將著重介紹一些前端二進(jìn)制數(shù)據(jù)處理相關(guān)的API知識,如Blob、File、FileReader、ArrayBuffer、TypeArray、DataView等等。
字節(jié)
在介紹各種API之前,我們需要先了解下和字節(jié)有關(guān)的知識。
我們知道,計(jì)算機(jī)是二進(jìn)制的世界,而字節(jié)(byte)是計(jì)算機(jī)技術(shù)中關(guān)于二進(jìn)制數(shù)據(jù)的一種基本單位,1字節(jié)有8個(gè)二進(jìn)制位,即8比特(bit)。
比特又叫位,一位二進(jìn)制數(shù)據(jù)要么是0、要么是1,只有兩種狀態(tài),所以1比特有2種狀態(tài)。
1字節(jié)有8比特,即8個(gè)二進(jìn)制位,那就能表示 2**8 = 256
種狀態(tài),取值從 00000000 到 11111111。
字節(jié)作為基本單位,在很多地方都被使用,如字符編碼知識,見前文前端需要搞懂的字符編碼:ASCII、Unicode、UTF8、UTF16等。
二進(jìn)制數(shù)據(jù)在存儲(chǔ)的時(shí)候,以字節(jié)為單位,這里還涉及到一個(gè)關(guān)于字節(jié)序的知識。
字節(jié)序
字節(jié)序描述的是計(jì)算機(jī)如何存儲(chǔ)字節(jié)。
因?yàn)槲覀冎?,?nèi)存存儲(chǔ)都有索引地址,每個(gè)字節(jié)對應(yīng)一個(gè)索引地址。一個(gè)字節(jié)存儲(chǔ)8位二進(jìn)制,即0到255之間,但需要存儲(chǔ)大于255的數(shù)值的時(shí)候,就需要多個(gè)字節(jié),多個(gè)字節(jié)就涉及到排序問題。
所以字節(jié)序就是:當(dāng)需要多個(gè)字節(jié)表示一個(gè)值的時(shí)候,這多個(gè)字節(jié)使用什么樣的排序方式在內(nèi)存中進(jìn)行存儲(chǔ)。
而排序方式主要是兩種:大端存儲(chǔ)(big-endian)和小端存儲(chǔ)(little-endian)。
大端存儲(chǔ)和小端存儲(chǔ)
大端存儲(chǔ)又稱大字節(jié)序、高字節(jié)序,方式是低位字節(jié)排在內(nèi)存中的高地址端,高字節(jié)位排放在內(nèi)存中的低地址端。圖片文件 png、jpg都是這種方式。
小端存儲(chǔ)又稱為小字節(jié)序、低字節(jié)序,方式是低位字節(jié)排在內(nèi)存中的低地址端,高位字節(jié)排在內(nèi)存中的高地址端。圖片文件gif是小端序。
示例
當(dāng)我們使用不同的字節(jié)序存儲(chǔ)數(shù)字 0x12345678
(這里是16進(jìn)制表示,對應(yīng)的十進(jìn)制:305419896。進(jìn)制相關(guān)知識可見前文Javascript中的進(jìn)制和進(jìn)制轉(zhuǎn)換):
大端存儲(chǔ)在內(nèi)存中的存儲(chǔ)地址:
小端存儲(chǔ)在內(nèi)存中的存儲(chǔ)地址:
這里數(shù)字字節(jié)的高-低位是從左到右,最高位是 12
,最低位是 78
;而內(nèi)存中存儲(chǔ)時(shí)從左到右是低地址——高地址。
所以在大端序中高位字節(jié)的 12
在內(nèi)存最左邊的低地址位,而低字節(jié)位 78
則在內(nèi)存最右邊的高地址位;而小端序則正好相反。
從視覺習(xí)慣上,大端存儲(chǔ)似乎更順眼,但無論哪種方式,計(jì)算的結(jié)果都是一樣的,只是在計(jì)算的時(shí)候需要處理這個(gè)排序方式,下文會(huì)涉及到。
Blob
Blob,即 Binary large Object,本質(zhì)上是一個(gè)二進(jìn)制對象,該對象表示的是一個(gè)不可變、原始數(shù)據(jù)的類文件對象。
它的不可變,代表它是只讀的,不可被改變。
Blob對象的構(gòu)造函數(shù)語法:new Blob(array, options)
。
參數(shù)array:是一個(gè)數(shù)據(jù)數(shù)組,可以是多種對象的數(shù)據(jù),包含 ArrayBuffer、Blob、String 等等。
參數(shù)options:可選對象,指定兩個(gè)屬性:
type
表示Blob對象數(shù)據(jù)的MIME類型;endings
指定包含行結(jié)束符\n的字符串如何寫入。
我們可以使用構(gòu)造函數(shù)直接創(chuàng)建一個(gè)新的 Blob 對象:
const blob = new Blob(['123456789'], {type : 'text/plain'});
新創(chuàng)建的對象實(shí)例,結(jié)構(gòu)如下:
從以上示例,我們就可以看到Blob對象的方法和屬性:
實(shí)例屬性
- size:Blob對象中數(shù)據(jù)的字節(jié)大小
- type:字符串,表示Blob對象數(shù)據(jù)的MIME類型
示例方法
1.arrayBuffer():返回包含Blob所有內(nèi)容的二進(jìn)制格式的ArrayBuffer的一個(gè)promise對象
2.stream():返回能讀取Blob的ReadableStream對象
3.text():返回包含Blob所有內(nèi)容的字符串(UTF-8編碼)的一個(gè)promise對象
4.slice([start [, end [, contentType]]]):
- 該方法有三個(gè)可選參數(shù),可用于分割Blob數(shù)據(jù)
- 它根據(jù)指定的起始和結(jié)束位置,返回原Blob在該范圍的數(shù)據(jù),得到一個(gè)新的Blob對象
- 第三個(gè)參數(shù)
contentType
可以為新Blob對象指定自己的MIME類型
可以針對上面的 blob
實(shí)例進(jìn)行操作:
blob.slice(0, 3).text().then(res => { console.log(res) }) // 結(jié)果:123
以上代碼,使用slice()方法獲取原blob的前三位的數(shù)據(jù),生成新的Blob實(shí)例后,通過text()方法打印出文本內(nèi)容。
下面可以看看Blob在接口請求中的應(yīng)用,F(xiàn)etch API中的 Response
對象,擁有一個(gè)blob方法,能夠得到Blob對象。
const imgRequst = new Request('11.jpg') fetch(imgRequst).then((response) => { return response.blob() }).then((mBlob) => { console.log(mBlob) })
通過以上代碼,請求一個(gè)jpg圖片文件,響應(yīng)對象通過 blob()
方法轉(zhuǎn)為Blob對象:
File
File對象繼承了Blob對象,是一種特殊類型的Blob,它擴(kuò)展了對系統(tǒng)文件的支持能力。
File提供文件信息,并能夠在javascript中進(jìn)行訪問,一般在使用 <input>
標(biāo)簽選擇文件時(shí)返回,因?yàn)?<input>
標(biāo)簽允許選擇多個(gè)文件,這里返回的是文件列表 files
。
除了 <input>
標(biāo)簽以外,還有兩種方式返回File對象:
- 自由拖放操作生成的
DataTransfer
對象。 - 文件系統(tǒng)訪問API中的
FileSystemFileHandle
對象的getFile()
方法。
File的構(gòu)造函數(shù):new File(bits, name[, options])
。 有三個(gè)參數(shù):
bits:是一個(gè)數(shù)據(jù)數(shù)組,可以是多種對象的數(shù)據(jù),與Blob對象類似
name:文件名稱
options:可選屬性對象,包含兩個(gè)選項(xiàng)
- type:MIME類型字符串
- lastModified:時(shí)間戳,表示文件的最后修改時(shí)間
下面代碼,通過 <input>
標(biāo)簽讀取文件:
<input id="input-file" type="file" accept="image/*" />
document.getElementById('input-file').onchange = (e) => { const file = e.target.files[0] console.log(file) // ... }
這是一個(gè)簡單的圖片上傳,獲取到的file實(shí)例,控制臺(tái)打印出來:
通過上圖(chrome瀏覽器下),可以看到File繼承了Blob的素有屬性和方法:
屬性除了size和type以外,F(xiàn)ile還有自己的幾個(gè)屬性
- lastModified:只讀,時(shí)間戳,文件最后修改時(shí)間
- name:只讀,文件名 lastModifiedDate:只讀,文件最后修改時(shí)間的 Date 對象,該對象已廢棄
- webkitRelativePath:非標(biāo)準(zhǔn)屬性,返回path或URL
File沒有自己的實(shí)例方法,都繼承自Blob
對Blob和File的讀取
File繼承自Blob,都是只讀對象,除了使用slice分片以外,并沒有其他操作能力,所以如果對它們進(jìn)行處理需要借助其他的API。
主要用于操作Blob的API有:FileReader、URL.createObjectURL()、createImageBitmap()和XMLHttpRequest.send()。下面將介紹這幾種方式。
Blob和File都是 WebAPI
,是由瀏覽器環(huán)境提供的,而上面提到這四種對象也同樣是WebAPI。
FileReader
FileReader是用于異步讀取文件類型(或原始數(shù)據(jù)緩沖區(qū))的內(nèi)容,指定Blob或File對象為需要讀取的文件數(shù)據(jù)。
FileReader 不能在文件系統(tǒng)中用路徑名的方式讀取文件。
構(gòu)造函數(shù):new FileReader()
。
如果對文件處理功能開發(fā)較多,對FileReader對象應(yīng)該較熟,我們先看一個(gè)示例:
document.getElementById('input-file').onchange = (e) => { const file = e.target.files[0] const reader = new FileReader() reader.onload = async (event) => { const img = new Image() img.src = event.target.result } reader.readAsDataURL(file) }
以上代碼,就是很常用的,使用FileReader讀取一個(gè)圖片文件的Base64數(shù)據(jù),然后使用圖片對象加載。Base64知識,可參考前文深入理解Base64編碼字符串。
這段代碼也涉及到FileReader對像的屬性、事件、方法。
FileReader的屬性 事件和方法
屬性(皆只讀)
error:在讀取文件時(shí)發(fā)生的錯(cuò)誤
readyState:表示當(dāng)前讀取狀態(tài)
常量名 | 值 | 狀態(tài)描述 |
---|---|---|
EMPTY | 0 | 沒有加載 |
LOADING | 1 | 正在加載 |
DONE | 2 | 已完成全部讀取 |
result:文件內(nèi)容,讀取狀態(tài)完成時(shí)才有效
方法
- abort():中止讀取操作。在返回時(shí),readyState屬性為DONE
- readAsArrayBuffer():以ArrayBuffer類型讀取Blob中的內(nèi)容
- readAsBinaryString():以原始二進(jìn)制數(shù)據(jù)類型讀取Blob中的內(nèi)容
- readAsDataURL():以Base64字符串類型讀取Blob中的內(nèi)容
- readAsText():以文本字符串類型讀取Blob中的內(nèi)容
事件
- onabort:讀取操作被中斷時(shí)觸發(fā)
- onerror:讀取操作發(fā)生錯(cuò)誤時(shí)觸發(fā)
- onload:讀取操作完成時(shí)觸發(fā)
- onloadstart:讀取操作開始時(shí)觸發(fā)
- onloadend:讀取操作結(jié)束時(shí)觸發(fā)
- onprogress:讀取Blob時(shí)觸發(fā)
URL.createObjectURL()
URL是瀏覽器環(huán)境提供的,用于處理url鏈接的一個(gè)接口對象??梢酝ㄟ^它,解析、構(gòu)造、規(guī)范和編碼各種url鏈接。
而URL提供的一個(gè)靜態(tài)方法 createObjectURL()
,可以用來處理Blob和File文件對象。
先看一個(gè)例子:
document.getElementById('input-file').onchange = (e) => { const file = e.target.files[0] const url = URL.createObjectURL(file) const img = new Image() img.onload = () => { document.body.append(img) } img.src = url }
頁面展示:
這段代碼就實(shí)現(xiàn)了上傳圖片,通過 URL.createObjectURL
讀取后生成一個(gè)本地映射的url,再使用Image對象加載圖片。
通過查看頁面元素,可以看到新添加的圖片元素,它的src是一個(gè)類似鏈接的字符串:blob:http://localhost:8088/29c8f4a5-9b47-436f-8983-03643c917f1c
,通過這個(gè)字符串,圖片就能加載顯示出來。
再來看 createObjectURL()
,它返回一個(gè)包含給定的Blob或File對象的url,就可以當(dāng)做文件資源被加載。而這個(gè)url的生命周期和它的窗口同步,窗口關(guān)閉這個(gè)url就自動(dòng)釋放了。
這個(gè)url就是被稱為偽協(xié)議的Objct URL。
Object URL
Object URL 又被稱為Blob URL,一般使用Blob或File對象生成,通過 URL.createObjectURL()
方法創(chuàng)建一個(gè)唯一的URL。
Object URL的格式為:blob:origin/唯一標(biāo)識(uuid)
。
上面生成的URL字符串就符合這個(gè)格式:blob:http://localhost:8088/29c8f4a5-9b47-436f-8983-03643c917f1c
。
- origin 對應(yīng)的
http://localhost:8088/
,如果直接打開本地html文件,則origin為null。 - uuid 對應(yīng)
29c8f4a5-9b47-436f-8983-03643c917f1c
。
瀏覽器內(nèi)部會(huì)為生成Object URL保持一個(gè) URL
到 Blob
的映射,Blob是留存在內(nèi)存中,瀏覽器只有在卸載當(dāng)前窗口文檔時(shí)才會(huì)釋放。
如果要手動(dòng)釋放,則需要URL的另外一個(gè)靜態(tài)方法:URL.revokeObjectURL()
,它用于銷毀之前創(chuàng)建的URL實(shí)例,在合適的時(shí)機(jī)調(diào)用即可銷毀Object URL。
URL.revokeObjectURL(url)
XMLHttpRequest.send()
XMLHttpRequest.send(body):用于在XHR的HTTP請求中,發(fā)送數(shù)據(jù)體。
這里的body參數(shù),可以是多種數(shù)據(jù)類型,包括Blob對象。
const xhr = new XMLHttpRequest() xhr.send(new Blob())
createImageBitmap()
createImageBitmap(): 主要處理圖片資源,接受不同的圖片資源對象為參數(shù),并生成一個(gè)ImageBitmap對象。
這些參數(shù)就就可以是Blob和File對象。
ImageBitmap表示可以繪制在canvas上的位圖圖像。
createImageBitmap(file).then(imageBitmap => { const canvas = document.createElement('canvas') canvas.width = imageBitmap.width canvas.height = imageBitmap.height const ctx = canvas.getContext('2d') ctx.drawImage(imageBitmap, 0, 0) document.body.append(canvas) })
如上代碼,即可讀取圖片文件,使用canvas繪制。
ArrayBuffer
ArrayBuffer 對象表示通用的、固定長度的原始二進(jìn)制緩沖區(qū),它是一個(gè)字節(jié)數(shù)組,但不能直接操作它的內(nèi)容,而需要通過其他方式(如TypeArray或DataView等)進(jìn)行處理。
構(gòu)造函數(shù):new ArrayBuffer(length)
,返回一個(gè)指定大小的ArrayBuffer對象。
參數(shù)length:要?jiǎng)?chuàng)建的 ArrayBuffer 的字節(jié)大小。大于Number.MAX_SAFE_INTEGER(>= 2 ** 53)或?yàn)樨?fù)數(shù),則拋出一個(gè)RangeError異常。
下面我們先使用前面介紹的 FileReader
讀取一個(gè)文件的ArrayBuffer內(nèi)容:
document.getElementById('input-file').onchange = (e) => { const file = e.target.files[0] const reader = new FileReader() reader.onload = async (event) => { console.log(event.target.result) } reader.readAsArrayBuffer(file) }
控制臺(tái)日志打印輸出:
從上圖,可以看到ArrayBuffer的實(shí)例屬性和方法:
- byteLength:表示字節(jié)大小,不可改變
- slice(begin[, end]):根據(jù)指定位置范圍返回一個(gè)新的ArrayBuffer,可以分割A(yù)rrayBuffer。
ArrayBuffer還有靜態(tài)屬性和方法:
- ArrayBuffer.length:構(gòu)造函數(shù)的length屬性,值為1
- ArrayBuffer.isView(arg):如果參數(shù)是ArrayBuffer的視圖實(shí)例則返回true。
由于我們無法直接操作ArrayBuffer,所以需要使用其他對象來處理,下面將介紹其中兩種。
TypeArray
TypeArray,即類型化數(shù)組,它描述了二進(jìn)制數(shù)據(jù)緩沖區(qū)的一個(gè)類數(shù)組。TypeArray本身不是一個(gè)可用的對象,只是一個(gè)輔助的數(shù)據(jù)類型,作為所有類型數(shù)組的構(gòu)造原型,真正可用的類型數(shù)組包含了多種,如Int8Array、Uint8Array等。
常用的類型數(shù)組如下表所示:
對象 | 元素所占字節(jié)數(shù) | 取值范圍 | 描述 |
---|---|---|---|
Int8Array | 1 | -128 - 127 | 8 位有符號整型數(shù)組 |
Uint8Array | 1 | 0 - 255 | 8 位無符號整型數(shù)組 |
Uint8ClampedArray | 1 | 0 - 255 | 8 位無符號整型固定數(shù)組 |
Int16Array | 2 | -32768 - 32767 | 16 位有符號整型數(shù)組 |
Uint16Array | 2 | 0 - 65535 | 16 位無符號整型數(shù)組 |
Int32Array | 4 | -2147483648 - 2147483647 | 32 位有符號整型數(shù)組 |
Uint32Array | 4 | 0 - 4294967295 | 32 位無符號整型數(shù)組 |
Float32Array | 4 | 1.2×10**-38 to 3.4×10**38 | 32 位浮點(diǎn)數(shù)型數(shù)組 |
Float64Array | 8 | 5.0×10**-324 to 1.8×10**308 | 64 位浮點(diǎn)數(shù)型數(shù)組 |
BigInt64Array | 8 | -2**63 to 2**63-1 | 64 位有符號數(shù)型數(shù)組 |
BigUint64Array | 8 | 0 to 2**64-1 | 64 位無符號整型數(shù)組 |
類型化數(shù)組與普通數(shù)據(jù)也較相似,同樣擁有一系列的方法和屬性,但不支持 push
、pop
、shift
、unshift
、splice
等可以改變原數(shù)組的增刪改方法。
類型化數(shù)組由于定義了數(shù)據(jù)類型,則各元素必須是同類型的數(shù)據(jù),不能像普通數(shù)據(jù)那樣元素可以是不同類型;當(dāng)元素?cái)?shù)據(jù)類型固定統(tǒng)一時(shí),處理效率更優(yōu)。
各類型數(shù)組在構(gòu)造函數(shù)、屬性、方法等語法上相同,本節(jié)就以 Uint8Array
為例。
語法
Uint8Array構(gòu)造函數(shù):
new Uint8Array() new Uint8Array(length) new Uint8Array(typedArray) new Uint8Array(object) new Uint8Array(buffer [, byteOffset [, length]])
靜態(tài)屬性和方法:
- BYTES_PER_ELEMENT:返回?cái)?shù)組元素所占字節(jié)數(shù),Uint8Array中的值是1,Uint32Array中的值是4,見上表
- length:固定長度,Uint8Array中的值是1,Uint32Array中的值是3,基本沒用
- name:類型數(shù)組返回自己的構(gòu)造名,Uint8Array類型返回
Uint8Array
,Uint32Array類型返回Uint32Array
等等 - from(source[, mapFn[, thisArg]]):從源類型數(shù)組中返回一個(gè)新的數(shù)組
- of(element0[, element1[, ...[, elementN]]]):創(chuàng)建一個(gè)具有可變數(shù)量參數(shù)的新類型數(shù)組
實(shí)例屬性和方法
介紹完靜態(tài)屬性和方法,下面通過一個(gè)示例,來查看下Uint8Array的實(shí)例屬性和方法,代碼如下。
const reader = new FileReader() reader.onload = async (event) => { const aBuffer = event.target.result const uint8Array = new Uint8Array(aBuffer) console.log(uint8Array) } reader.readAsArrayBuffer(file)
以上代碼,直接讀取文件的ArrayBuffer數(shù)據(jù),然后通過 Uint8Array
構(gòu)造函數(shù),得到Uint8Array實(shí)例,控制臺(tái)查看:
通過加載一張png圖片,得到它的Uint8Array數(shù)組數(shù)據(jù),可以看到類型數(shù)組大部分的屬性和方法都和普通數(shù)組類似,除了前文提到的增刪改數(shù)組的方法以外。因此,對類型數(shù)組使用下標(biāo)、循環(huán)等等方式進(jìn)行讀取,和普通函數(shù)沒什么兩樣。
而類型數(shù)組也自己的特殊屬性(都只讀)和方法,如下:
- buffer:返回類型數(shù)組引用的ArrayBuffer
- byteLength:字節(jié)數(shù)長度
- byteOffset:相對源ArrayBuffer的偏移字節(jié)數(shù)
- length:數(shù)組長度
- set(array[, offset]):從給定數(shù)組中讀取元素值,并存儲(chǔ)在類型數(shù)組中
- subarray(begin, end):給定開始和結(jié)尾索引,返回一個(gè)新的類型數(shù)組
類型數(shù)組間的關(guān)系
要了解常見類型數(shù)組間的關(guān)系,我們先看下面這張圖:
圖上所示,是一張png圖片的ArrayBuffer數(shù)據(jù),可以看到,ArrayBuffer的字節(jié)長度屬性默認(rèn)取8位整型數(shù)組的長度,即與Int8Array和Uint8Array的長度一致。
而Int8Array的長度29848,正好是Int16Array的長度14924的兩倍,是Int32Array的長度7462的四倍,可知,這里就是對字節(jié)的合并計(jì)算:
- Int8Array(Uint8Array) 轉(zhuǎn) Int16Array(Uint16Array),需要依序合并兩個(gè)字節(jié)后計(jì)算數(shù)值。
- Int8Array(Uint8Array) 轉(zhuǎn) Int32Array(Uint32Array),需要依序合并四個(gè)字節(jié)后計(jì)算數(shù)值。
- Int16Array(Uint16Array) 轉(zhuǎn) Int32Array(Uint32Array),需要依序合并兩個(gè)字節(jié)后計(jì)算數(shù)值。
讀取GIF文件示例
類型數(shù)組通過數(shù)組的方式對ArrayBuffer的內(nèi)容進(jìn)行讀取操作,可以方便我們處理文件的二進(jìn)制數(shù)據(jù)。 但使用類型數(shù)組的時(shí)候,碰到多字節(jié)的數(shù)據(jù)時(shí),需要考慮字節(jié)序的問題。
下面,我們以讀取小端存儲(chǔ)的GIF圖片為例。
GIF圖片的Uint8Array數(shù)組數(shù)據(jù)中,寬高數(shù)據(jù)的存儲(chǔ)就是使用了兩個(gè)字節(jié),第7-8位存儲(chǔ)圖片的寬度,9-10位存儲(chǔ)圖片的高度。
我們加載的GIF圖片寬高皆為600,需要處理字節(jié)序,代碼如下:
const uint8Array = new Uint8Array(aBuffer) let bufferIndex = 6 // 獲取GIF寬度的兩個(gè)字節(jié)的值 const width1 = uint8Array[bufferIndex] // width1 結(jié)果:88 const width2 = uint8Array[bufferIndex + 1] // width2 結(jié)果:2 // 得到各自的16進(jìn)制數(shù)據(jù) const width1hex = width1.toString(16) const width2hex = width2.toString(16) // 轉(zhuǎn)換成實(shí)際的寬度大小,注意這里把兩個(gè)字節(jié)的順序做了調(diào)整,符合小端序 const width = parseInt(width2hex + width1hex, 16) // width 結(jié)果:600
使用小端序處理后,寬度結(jié)果等于600,符合圖片實(shí)際寬度。
自己手動(dòng)處理字節(jié)序會(huì)稍顯麻煩,如果不想手動(dòng)去處理字節(jié)序的問題,可以使用另外一個(gè)對象:DataView
。
DataView
DataView 是一個(gè)從 ArrayBuffer
中讀取多種類型數(shù)值并且不用考慮字節(jié)序的接口對象。它的使用簡單方便,擁有一系列的 get-
和 set-
實(shí)例方法操作數(shù)據(jù)。
DataView的構(gòu)造函數(shù):new DataView(buffer [, byteOffset [, byteLength]])
。 參數(shù):
- buffer:源ArrayBuffer
- byteOffset:buffer中的字節(jié)偏移量
- byteLength:字節(jié)長度
DataView不用考慮字節(jié)序,同樣是讀取GIF的寬度時(shí),代碼可簡化:
const fileDataView = new DataView(arrBuffer) let bufferIndex = 6 const width = fileDataView.getUint16(bufferIndex, true) // 結(jié)果:600 bufferIndex += 2 const height = fileDataView.getUint16(bufferIndex, true) // 結(jié)果:600
以上代碼,很方便就得到GIF圖片的寬高數(shù)據(jù)(600),因?yàn)槭褂昧?DataView
和它的 getUint16
方法,不需要手動(dòng)處理字節(jié)序。
getUint16
方法有兩個(gè)參數(shù):第一個(gè)參數(shù)代表字節(jié)索引;第二參數(shù)表示字節(jié)序,默認(rèn)大端序,為true則是小端序,GIF是小端,所以上面代碼為true。
除了getUint16
以外,DataView
還有十多個(gè)類似的實(shí)例方法。
DataView的get和set系列方法
get系列方法通過字節(jié)偏移索引獲取對應(yīng)的數(shù)值,其中多字節(jié)的數(shù)據(jù),需要兩個(gè)參數(shù):
- byteOffset:讀取時(shí)的字節(jié)偏移量
- littleEndian:字節(jié)序,默認(rèn)大端,設(shè)為true則是小端
名稱 | 參數(shù) | 描述 |
---|---|---|
getInt8 | (byteOffset) | 有符號 8-bit 整數(shù)(1個(gè)字節(jié)) |
getUint8 | (byteOffset) | 無符號 8-bit 整數(shù)(1個(gè)字節(jié)) |
getInt16 | (byteOffset [, littleEndian]) | 16-bit數(shù)(短整型,2個(gè)字節(jié)) |
getUint16 | (byteOffset [, littleEndian]) | 16-bit數(shù)(無符號短整型,2個(gè)字節(jié)) |
getInt32 | (byteOffset [, littleEndian]) | 32-bit數(shù)(長整型,4個(gè)字節(jié)) |
getUint32 | (byteOffset [, littleEndian]) | 32-bit數(shù)(無符號長整型,4個(gè)字節(jié)) |
getFloat32 | (byteOffset [, littleEndian]) | 32-bit浮點(diǎn)數(shù)(單精度浮點(diǎn)數(shù),4個(gè)字節(jié)) |
getFloat64 | (byteOffset [, littleEndian]) | 64-bit數(shù)(雙精度浮點(diǎn)型,8個(gè)字節(jié)) |
getBigInt64 | (byteOffset [, littleEndian]) | 帶符號的64位整數(shù)(long long類型)值 |
getBigUint64 | (byteOffset [, littleEndian]) | 無符號的64位整數(shù)(unsigned long long類型)值 |
set系列方法是和get方法對應(yīng)的,處理相應(yīng)字節(jié)偏移索引位置的數(shù)值,參數(shù)如下:
- byteOffset:讀取時(shí)的字節(jié)偏移量
- value:設(shè)置相應(yīng)類型的數(shù)值
- littleEndian:字節(jié)序,默認(rèn)大端,設(shè)為true則是小端
名稱 | 參數(shù) | 描述 |
---|---|---|
setInt8 | (byteOffset, value) | 8-bit數(shù)(一個(gè)字節(jié)) |
setUint8 | (byteOffset, value) | 8-bit數(shù)(無符號字節(jié)) |
setInt16 | (byteOffset, value [, littleEndian]) | 16-bit數(shù)(短整型) |
setUint16 | (byteOffset, value [, littleEndian]) | 16-bit數(shù)(無符號短整型) |
setInt32 | (byteOffset, value [, littleEndian]) | 32-bit數(shù)(長整型) |
setUint32 | (byteOffset, value [, littleEndian]) | 32-bit數(shù)(無符號長整型) |
setFloat32 | (byteOffset, value [, littleEndian]) | 32-bit數(shù)(浮點(diǎn)型) |
setFloat64 | (byteOffset, value [, littleEndian]) | 64-bit數(shù)(雙精度浮點(diǎn)型) |
setBigInt64 | (byteOffset, value [, littleEndian]) | 帶符號的64位整數(shù)(long long類型)值 |
setBigUint64 | (byteOffset, value [, littleEndian]) | 無符號的64位整數(shù)(unsigned long long類型)值 |
Blob和ArrayBuffer
對于Blob和ArrayBuffer兩個(gè)對象,我們可以稍做總結(jié):
- Blob是Web API,瀏覽器環(huán)境提供,讀取它可以使用FileReader、URL.createObjectURL等WebAPI;ArrayBuffer是JS語言內(nèi)置對象,處理它則需要使用TypeArray、DataView等JS-API。
- Blob表示不可變的類文件數(shù)據(jù);ArrayBuffer則表示原始數(shù)據(jù)緩沖區(qū)。
- Blob用于讀取類文件數(shù)據(jù),不對應(yīng)內(nèi)存;ArrayBuffer用于讀取內(nèi)存數(shù)據(jù)。
- Blob和ArrayBuffer都需要通過其他對象才能操作數(shù)據(jù)。
- Blob和ArrayBuffer可以使用不同方式進(jìn)行相互之間的轉(zhuǎn)換。
- 要操作字節(jié)二進(jìn)制數(shù)據(jù),得依賴ArrayBuffer和輔助它的操作對象。
Blob和ArrayBuffer之間的轉(zhuǎn)換:
- 使用Blob構(gòu)造函數(shù)可以讀取ArrayBuffer,生成一個(gè)新的Blob。
- 通過Blob實(shí)例的arrayBuffer()方法,可以獲取到對應(yīng)的ArrayBuffer。
- 通過FileReader對象的readAsArrayBuffer()方法,將Blob讀取為ArrayBuffer。
如下代碼,進(jìn)行二者之間的轉(zhuǎn)換:
const aBuffer = new ArrayBuffer(4) // 使用Blob構(gòu)造函數(shù) const blob = new Blob([aBuffer]) // Blob的arrayBuffer()方法(promise) blob.arrayBuffer() // FileReader const reader = new FileReader() reader.readAsArrayBuffer(blob)
以上就是深入理解JavaScript字節(jié)二進(jìn)制知識以及相關(guān)API的詳細(xì)內(nèi)容,更多關(guān)于JavaScript字節(jié)二進(jìn)制的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
RGB轉(zhuǎn)換實(shí)現(xiàn)代碼,淘寶前端開發(fā)工程師筆試題
寫一個(gè)轉(zhuǎn)換RGB的值的函數(shù),實(shí)現(xiàn)以下效果。2010-11-11JavaScript中判斷數(shù)據(jù)類型的方法總結(jié)
比如要判斷一個(gè)變量是否是數(shù)組類型,PHP中有is_array()函數(shù)可以直接判斷,然而js中我們需要...-- well,下面我們就來詳細(xì)看一下JavaScript中判斷數(shù)據(jù)類型的方法總結(jié)2016-05-05基于Turn.js 實(shí)現(xiàn)翻書效果實(shí)例解析
最近項(xiàng)目經(jīng)理我個(gè)項(xiàng)目練練手,其項(xiàng)目需求是要實(shí)現(xiàn)翻書效果,看到這個(gè)需求后,我真是懵了,這咋整,我可是java出身的啊,這個(gè)問題真是難住我了,后來有同事的指導(dǎo),問題順利解決,下面小編把學(xué)習(xí)心得分享,感興趣的朋友可以參考下2016-06-06JavaScript中檢查對象property的存在性方法介紹
這篇文章主要介紹了JavaScript中檢查對象property的存在性方法介紹,本文講解了4種方法來檢查某個(gè)對象o是否擁有property x,需要的朋友可以參考下2014-12-12js為鼠標(biāo)添加右擊事件防止默認(rèn)的右擊菜單彈出
本文為大家介紹下如何為使用js為鼠標(biāo)添加右擊事件防止默認(rèn)的右擊菜單彈出,感興趣的朋友可以參考下,希望對大家有所幫助2013-07-07Pro JavaScript Techniques學(xué)習(xí)筆記
Pro JavaScript Techniques學(xué)習(xí)筆記,學(xué)習(xí)js的朋友可以參考下。2010-12-12JS數(shù)組push、unshift、pop、shift方法的實(shí)現(xiàn)與使用方法示例
這篇文章主要介紹了JS數(shù)組push、unshift、pop、shift方法,結(jié)合實(shí)例形式分析了JS數(shù)組push、unshift、pop、shift方法針對數(shù)組添加、刪除等相關(guān)操作技巧,需要的朋友可以參考下2020-04-04