Visual LabINTERACTIVE LEARNING
JavaScript 内核第 2 / 7 章

类型化数组、Blob 与 File

理解 ArrayBuffer、TypedArray 视图以及浏览器二进制文件对象。

this原型异步
进阶预计 65 分钟查看源文 ↗

类型数组详解

参考资料 ArrayBuffer,二进制数组

ArrayBuffer

提示

是基本的二进制对象, 对固定长度的连续内存空间的引用

创建
let buffer = new ArrayBuffer(16); // 创建一个长度为 16 的 buffer
alert(buffer.byteLength); // 16
// 它会分配一个 16 字节的连续内存空间,并用 0 进行预填充。
注意

提示

让我们先澄清一个可能的误区。ArrayBufferArray 没有任何共同之处:

  • 它的长度是固定的,我们无法增加或减少它的长度。
  • 它正好占用了内存中的那么多空间。
  • 要访问单个字节,需要另一个“视图”对象,而不是 buffer[index]

ArrayBuffer 是一个内存区域,它里面存储了一个原始的字节序列。

视图对象

视图对象的概念

提示

**“视图”对象 是操作 ArrayBuffer的工具,**本身并不存储任何东西,而是通过它的规则解释存储在 ArrayBuffer 中的字节,通用术语是 TypedArray

  • Uint8Array

ArrayBuffer 中的每个字节视为 0 到 255 之间的单个数字(每个字节是 8 位,因此只能容纳那么多)。这称为 “8 位无符号整数”。

  • Uint16Array

将每 2 个字节视为一个 0 到 65535 之间的整数。这称为 “16 位无符号整数”。

  • Uint32Array

将每 4 个字节视为一个 0 到 4294967295 之间的整数。这称为 “32 位无符号整数”。

  • Float64Array

将每 8 个字节视为一个 5.0x10(-324)1.8x10(308) 之间的浮点数。

因此,一个 16 字节 ArrayBuffer 中的二进制数据可以解释为 16 个“小数字”,或 8 个更大的数字(每个数字 2 个字节),或 4 个更大的数字(每个数字 4 个字节),或 2 个高精度的浮点数(每个数字 8 个字节)

TypedArray属性和方法
创建方法
// 以上的几种类型数组,都有以下5中函数重载的实现,Uint32Array
new TypedArray(buffer, [byteOffset], [length]);  // 除了提供buffer外,其他情况都会创建新的buffer
new TypedArray(object);
new TypedArray(typedArray);
new TypedArray(length);
new TypedArray();
属性
  • arr.buffer —— 引用 ArrayBuffer
  • arr.byteLength —— ArrayBuffer 的长度
越界行为

对于越界值,会采用仅保留最右位,多余的位被切除的操作

Uint8``Clampe``dArray 在这方面比较特殊,它的表现不太一样。对于大于 255 的任何数字,它将保存为 255,对于任何负数,它将保存为 0。此行为对于图像处理很有用。

方法

TypedArray 具有常规的 Array 方法,但有个明显的例外。

我们可以遍历(iterate),mapslicefindreduce 等。

但有几件事我们做不了:

  • 没有 splice —— 我们无法“删除”一个值,因为类型化数组是缓冲区(buffer)上的视图,并且缓冲区(buffer)是固定的、连续的内存区域。我们所能做的就是分配一个零值。
  • concat 方法。

还有两种其他方法:

  • arr.set(fromArr, [offset])offset(默认为 0)开始,将 fromArr 中的所有元素复制到 arr
  • arr.subarray([begin, end]) 创建一个从 beginend(不包括)相同类型的新视图。这类似于 slice 方法(同样也支持),但不复制任何内容 —— 只是创建一个新视图,以对给定片段的数据进行操作。
DataView`` 更灵活的TypedArray
DataView 是在 ArrayBuffer 上的一种特殊的超灵活“未类型化”视图。它允许以任何格式访问任何偏移量(offset)的数据。
  • 对于类型化的数组,构造器决定了其格式。整个数组应该是统一的。第 i 个数字是 arr[i]
  • 通过 DataView,我们可以使用 .getUint8(i).getUint16(i) 之类的方法访问数据。我们在调用方法时选择格式,而不是在构造的时候。

语法:

new DataView(buffer, [byteOffset], [byteLength])
  • buffer —— 底层的 ArrayBuffer。与类型化数组不同,DataView 不会自行创建缓冲区(buffer)。我们需要事先准备好。
  • byteOffset —— 视图的起始字节位置(默认为 0)。
  • byteLength —— 视图的字节长度(默认至 buffer 的末尾)。
/ 4 个字节的二进制数组,每个都是最大值 255
let buffer = new Uint8Array([255, 255, 255, 255]).buffer;

let dataView = new DataView(buffer);

// 在偏移量为 0 处获取 8 位数字
alert( dataView.getUint8(0) ); // 255

// 现在在偏移量为 0 处获取 16 位数字,它由 2 个字节组成,一起解析为 65535
alert( dataView.getUint16(0) ); // 65535(最大的 16 位无符号整数)

// 在偏移量为 0 处获取 32 位数字
alert( dataView.getUint32(0) ); // 4294967295(最大的 32 位无符号整数)

dataView.setUint32(0, 0); // 将 4 个字节的数字设为 0,即将所有字节都设为 0

可以自由操作二进制数字了

Blob 和File

Blob

💡 提示

Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作

构造函数
  • blobParts 可选一个可迭代对象,比如 Array,包含 ArrayBufferTypedArrayDataViewBlob、字符串或者任意这些元素的混合,这些元素将会被放入 Blob 中。

  • options 可选一个可以指定以下任意属性的对象:

    • type 可选将会被存储到 blob 中的数据的 MIME 类型。默认值是空字符("")。
    • endings 可选 非标准如果数据是文本,那么如何解释其中的换行符(\n)。默认值 transparent 会将换行符复制到 blob 中而不会改变它们。要将换行符转换为主机系统的本地约定,请指定值 native
  • 返回值

    • 一个包含指定数据的新的 Blob 对象。
实例属性、方法
实例属性
  • Blob.size 只读Blob 对象中所包含数据的大小(字节)。
  • Blob.type 只读一个字符串,表明该 Blob 对象所包含数据的 MIME 类型。如果类型未知,则该值为空字符串。
实例方法
  • Blob.arrayBuffer()返回一个 promise,其会兑现一个包含 Blob 所有内容的二进制格式的 ArrayBuffer
  • Blob.slice()返回一个新的 Blob 对象,其中包含调用它的 blob 的指定字节范围内的数据。
  • Blob.stream()返回一个能读取 Blob 内容的 ReadableStream
  • Blob.text()返回一个 promise,其会兑现一个包含 Blob 所有内容的 UTF-8 格式的字符串。
const obj = { hello: "world" };
const blob = new Blob([JSON.stringify(obj, null, 2)], {
  type: "application/json",
});
读取方式

Blob 对象中提取数据有几种不同的方法,每种方法都适用于不同的场景和数据类型。下面是 arrayBuffer()FileReaderBlob.text()Response(blob).text() 方法的区别:

  1. Blob.arrayBuffer():

    • 这个方法返回一个 Promise,它解析为一个 ArrayBuffer 对象。
    • 它适用于任何类型的 Blob 数据,不仅限于文本。
    • 它不关心 Blob 的类型,只将数据作为原始二进制数据返回。
  2. FileReader:

    • FileReader 是一个更通用的接口,可以用来读取文件或 Blob 数据。
    • 它支持多种读取模式,例如 readAsText(), readAsDataURL(), 和 readAsArrayBuffer()
    • 使用 FileReader 时,你可以指定编码(仅当读取文本时)。
    • FileReader 通常用于读取文件输入字段中的文件。
  3. Blob.text():

    • 这个方法是 Blob 对象的一个便利方法,返回一个 Promise,解析为一个字符串。
    • 它专门用于读取文本数据,自动将 Blob 中的数据解码为 UTF-8 字符串。
    • 它适用于文本类型的 Blob,例如 JSON、XML 或纯文本。
  4. Response(blob).text():

    • 这个方法是 Response 对象的一个方法,用于从 Response 对象中提取文本数据。
    • 当你从 fetch API 接收到一个包含 Blob 类型的响应时,可以使用 Response.text() 来获取响应体的文本表示。
    • 它与 Blob.text() 类似,但 Response.text() 可以处理来自网络请求的响应体。

总结来说,选择哪种方法取决于你需要处理的数据类型和上下文:

  • 如果你需要处理二进制数据,使用 Blob.arrayBuffer()
  • 如果你需要读取文本数据,并且 Blob 类型已知,使用 Blob.text()Response.text()
  • 如果你需要更多的灵活性,例如不同的编码或不同的读取模式,使用 FileReader

File

继承自Blob,是它的一个扩展