1

理解JavaScript中各种二进制对象关系

 2 years ago
source link: https://segmentfault.com/a/1190000040614613
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

现代 JavaScript 要面临更加复杂的场景,对于各种类型的数据传输也多了起来,其中涉及二进制传输,为了方便处理数据提高效率于是创造了ArrayBuffer对象。

但是使用中会发现不仅仅有ArrayBuffer,还有TypedArrayDataViewBlobFileReader等一系列对象,让人迷惑它们之间关系是什么?为什么有这么多的对象?带着问题查询了资料,试着梳理其中的关系。

各种对象的关系

ArrayBuffer

ArrayBuffer是 JavaScript 最基本的处理二进制的对象,描述的是一段连续的内存空间,其单位是字节(byte)。

const buffer = new ArrayBuffer(32);

这样我们就创建了一块 32 字节的内存区域,可以使用buffer.byteLength来查看其长度。

ArrayBuffer对象能做的操作不多,并且是不可编辑的。如果需要编辑数据,要利用另外两个对象TypedArrayDataView

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 位无符号整数

创建的时候可以传入长度typedArrayArrayBuffer数组。当然也可以什么都不传入。

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);

提供了getInt8getUint8getInt16getUint16getInt32getUint32getFloat32getFloat64方法。

参数有两个,第一位是节序位置,第二位是字节序,非必填。返回值是相应位置的字节数据。

const d1 = dataView.getUint8(1);
const d2 = dataView.getUint8(1, true);

字节位置好理解,字节序可以阅读《理解字节序》,总的说就是:

  • 大端字节序(big endian):高位字节在前,低位字节在后,这是人类读写数值的方法。
  • 小端字节序(little endian):低位字节在前,高位字节在后,即以 0x1122 形式储存。

默认情况下使用的是大端字节序,如果要使用小端字节序需要传入true

这样我们就有了基础的二进制的读写方案。可实际的应用场景中往往有更加复杂的数据,所以又针对专门的场景又衍生出BlobFileReader等对象。

Blob

Blob,是Binary Large Object(二进制大型对象)的缩写。

ArrayBuffer差异是,ArrayBuffer是单纯的二进制数据,而Blob是带MIME类型的二进制数据。并且可以方便的从StringArrayBufferTypedArrayDataViewBlob生成为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片段。

在开发中我们获取到图片二进制数据,我们可以转换成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 教程 浏览器模型相关章节


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK