理解JavaScript中各种二进制对象关系
现代 JavaScript 要面临更加复杂的场景,对于各种类型的数据传输也多了起来,其中涉及二进制传输,为了方便处理数据提高效率于是创造了ArrayBuffer
对象。
但是使用中会发现不仅仅有ArrayBuffer
,还有TypedArray
、DataView
、Blob
、FileReader
等一系列对象,让人迷惑它们之间关系是什么?为什么有这么多的对象?带着问题查询了资料,试着梳理其中的关系。
各种对象的关系
ArrayBuffer
ArrayBuffer
是 JavaScript 最基本的处理二进制的对象,描述的是一段连续的内存空间,其单位是字节(byte
)。
const buffer = new ArrayBuffer(32);
这样我们就创建了一块 32 字节的内存区域,可以使用buffer.byteLength
来查看其长度。
ArrayBuffer
对象能做的操作不多,并且是不可编辑的。如果需要编辑数据,要利用另外两个对象TypedArray
与DataView
。
TypedArray
TypedArray
类型化数组,TypedArray
本身不存储任何数据,只是专门用来查看ArrayBuffer
数据,所以称之为,TypedArray
不是某一个构造函数名,而是一组构造函数的统称。
Int8Array
:1 比特,8 位有符号整数Uint8Array
:1 比特,8 位无符号整数Uint8ClampedArray
:1 比特,8 位无符号整数Int16Array
:2 比特,16 位无符号整数Uint16Array
:2 比特,16 位无符号整数Int32Array
:4 比特,32 位无符号整数Uint32Array
:4 比特,32 位无符号整数Float32Array
:4 比特,32 位无 IEEE 浮点数Float64Array
:8 比特,64 位无 IEEE 浮点数BigInt64Array
:8 比特,64 为二进制有符号整数BigUint64Array
:8 比特,64 位无符号整数
创建的时候可以传入长度
、typedArray
、ArrayBuffer
、数组
。当然也可以什么都不传入。
const uint1 = new Uint8Array(8);
const uint2 = new Uint16Array(new Uint8Array(8));
const uint3 = new Uint8Array(new ArrayBuffer(8));
const uint4 = new Uint8Array([1, 2, 3]);
const uint5 = new Uint8Array();
以上typedArray
中,除了创建时传入ArrayBuffer
不会新创建ArrayBuffer
,其他在new
过程中底层都会创建新的ArrayBuffer
。可以使用arr.buffer
来访问其引用的ArrayBuffer
。
操作上普通数组的操作都能在TypedArray
中使用。但因为ArrayBuffer
描述的是连续的内存区间,所以我们无法删除某一个值,只能分配为0
,也没办法使用concat
方法。
Uint8ClampedArray
Uint8ClampedArray
相对特殊一点,在正负溢出的情况下处理不同。
其他对于存入越界数据仅保留最右边(低位)部分,抛弃溢出数据,而Uint8ClampedArray
对越界数据都保存为255
,对于传入的负数
保存为0
。
字符的相互转换
TypedArray
不支出直接传字符串,所以需要先转码一下。
String → Unit8Array
const string = "Hello";
Uint8Array.from(string.split(""), (e) => e.charCodeAt(0));
Unit8Array → String
// 使用TextDecoder对象
const u8 = Uint8Array.of(72, 101, 108, 108, 111);
new TextDecoder().decode(u8);
// 使用fromCharCode转换
const u8 = Uint8Array.of(72, 101, 108, 108, 111);
Array.from(u8, (e) => String.fromCharCode(e)).join("");
DataView
以上数据除了uint2
变量,其他都是单一的数据类型,uint2
对象这种一段内存中存放了两种类型数据,称之为复合视图
。JavaScript 中数据类型往往不那么单一,仅用TypedArray
操作会更加麻烦,所以又有了DataView
对象。DataView
相对于TypedArray
有着更加多种的操作方法。
const buffer = new ArrayBuffer(8);
const dataView = new DataView(buffer);
提供了getInt8
、getUint8
、getInt16
、getUint16
、getInt32
、getUint32
、getFloat32
、getFloat64
方法。
参数有两个,第一位是节序位置,第二位是字节序,非必填。返回值是相应位置的字节数据。
const d1 = dataView.getUint8(1);
const d2 = dataView.getUint8(1, true);
字节位置好理解,字节序可以阅读《理解字节序》,总的说就是:
- 大端字节序(big endian):高位字节在前,低位字节在后,这是人类读写数值的方法。
- 小端字节序(little endian):低位字节在前,高位字节在后,即以 0x1122 形式储存。
默认情况下使用的是大端字节序,如果要使用小端字节序需要传入true
。
这样我们就有了基础的二进制的读写方案。可实际的应用场景中往往有更加复杂的数据,所以又针对专门的场景又衍生出Blob
、FileReader
等对象。
Blob
Blob
,是Binary Large Object(二进制大型对象)
的缩写。
与ArrayBuffer
差异是,ArrayBuffer
是单纯的二进制数据,而Blob
是带MIME类型
的二进制数据。并且可以方便的从String
、ArrayBuffer
、TypedArray
、DataView
、Blob
生成为Blob
对象。
const blob1 = new Blob(["hello"], { type: "text/plain" });
const blob2 = new Blob(
[new Uint8Array([72, 101, 108, 108, 111]), " ", "world"],
{ type: "text/plain" }
);
属性:
size
:读取对象的字节大小。type
:读取写入的MIME类型
方法:
slice
:提取Blob
片段。
URL
在开发中我们获取到图片二进制数据,我们可以转换成base64
写入src
中,但如果数据量很大,或者视频数据,就会超过其允许长度。我们可以使用URL.createObjectURL
来方便的创建一个资源的 URL。
const url = URL.createObjectURL(blob1);
会生成类似blob:https://example.com/a6728d20-2e78-4497-9d6c-0ed61b93f11e
的资源 URL,可以直接写入src
中使用。
在不用时使用URL.revokeObjectURL(url)
销毁其引用,释放其内存占用。
数据读取
如果我们要查看其中数据的话,有两种方式。
第一种,使用Response
对象,可以直接读取字符串数据或是arrayBuffer
数据。
const responseText = await new Response(blob2).text();const responseBuf = await new Response(blob2).arrayBuffer();
第二种,使用FileReader
对象。
const reader = new FileReader();reader.onload = function (e) { console.log(reader.result);};reader.readAsText(blob2);
File
File
继承自Blob
,并增加了文件相关的属性信息。
name
:文件名lastModified
:最后修改时间的时间戳lastModifiedDate
:最后修改时间的Date
对象webkitRelativePath
:文件的路径。在 input 中选择目录时,会设置这个属性,非标准特性。
FileList
FileList
对象是File
对象的集合。一般出现在:
<input type="file">
控件,其中files
属性是一个FileList
- 拖拽事件中产生的DataTransfer对象,其中
files
属性会是一个FileList
属性:
length
:可以获取当前FileList
包含多少个File
方法:
item(index)
:可获取指定索引位置的File
数据,一般情况下直接使用FileList[index]
替代了。
FileReader
FileReader
在谈Blob
一节有提到过,实际上FileReader
对象就是专门用来读取Blob
对象的,当然也包括扩展的File
对象。
属性:
result
:文件的内容。readyState
:状态。0
:未加载;1
:正在加载;2
:加载完成。error
:加载数据时的错误信息。
事件:
onload
:加载成功后触发。onerror
:加载错误时触发。onabort
:加载中断时触发。onloadend
:加载结束后触发。onloadstart
:加载开始时触发。onprogress
:加载中触发。
方法:
readAsText(blob, "utf-8")
:以文本形式返回数据,第二个参数可设置文本编码。readAsDataURL(blob)
:以Data URL
的形式返回数据。readAsArrayBuffer(blob)
:以ArrayBuffer
形式返回数据。abort
:中止操作。
如上面的示例,就是以文本形式返回数据:
const reader = new FileReader();reader.onload = function (e) { console.log(reader.result);};reader.readAsText(blob2);
相关资料
MDN 相关的关键字
现代 JavaScript 教程 第三部分 二进制数据,文件
阮一峰 JavaScript 教程 浏览器模型相关章节
01小径
Chrome 历史版本下载
LnEoi阅读 61
正则表达式实例
寒青赞 56阅读 8.5k评论 11
JavaScript有用的代码片段和trick
jenemy赞 48阅读 7.1k评论 12
从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
乌柏木赞 75阅读 7.1k评论 16
再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
libinfs赞 42阅读 6.8k评论 12
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
乌柏木赞 45阅读 8.6k评论 6
从零搭建 Node.js 企业级 Web 服务器(二):校验
乌柏木赞 35阅读 6.7k评论 10
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。