ES6 中的 TypedArray 数据类型详解

阅读时长 6 分钟读完

在 JavaScript 编程中,TypedArray 作为数据类型是比较常用的。这种数据类型的出现只是为了能够更方便地操作二进制数据。它提供了一种可以用来认定视图的构造函数,得以读入已知的类型,而不是十六进制数值的数组。下面我们就来一一学习 ES6 中的 TypedArray 数据类型。

1. TypedArray

TypedArray 是指一种只含有成员为特定数据类型的数组,其中特定数据类型是由 TypedArray 类型定义的。该类型有以下几个常用的子类型:

  • Int8Array:8 位有符号整数
  • Uint8Array:8 位无符号整数
  • Uint8ClampedArray:8 位无符号整数(有约束)
  • Int16Array:16 位有符号整数
  • Uint16Array:16 位无符号整数
  • Int32Array:32 位有符号整数
  • Uint32Array:32 位无符号整数
  • Float32Array:32 位浮点数
  • Float64Array:64 位浮点数

我们需要注意的是,TypedArray 只能包含一个特定的数据类型,而不能同时包含多个不同类型的数据。

实现 TypedArray 的方式在其子类型内是基本一致的。在使用它们之前,必须要区分在构造函数中指定的 Buffers/ArrayBuffers 来存储数据以及这些 Buffer/ArrayBuffer 的字节偏移量(Buffer 不需要这个偏移量的信息)。

在 ES6 中使用 TypedArray 的一个常见的场景是处理音视频 Flow 的二进制数据。

2. Buffer/ArrayBuffer 特性

TypedArray 可以接受 ArrayBuffer 或 Buffer 这两个类型的数据源。 ArrayBuff 是一种可以让 TypedArray 映射到内存的字节数组,是无法直接使用的。而 Buffer 是 Node.js 赋予 JavaScript 的 Buffer 类,能够更加友好地支持二进制数据操作。

下面我们就来了解一下这两个类型的特性以及它们之间的区别。

2.1 ArrayBuffer

ArrayBuffer 是一个二进制数据缓存区,通常是不能直接操作的。如果你想在 ES5 或者之前的环境用 TypedArray 来读取二进制流,你需要自己实现 ArrayBuffer 的分配、增长、运维等。而到了 ES6 以后,基于 TypedArray 的语法糖就让 ArrayBuffer 操作变得简单了起来。

TypedArray 对 ArrayBuffer 实际上是要求对其进行一定程度的使用。其中,TypedArray 数组是在 ArrayBuffer 上导出的视图,以特定长度和字节偏移量构建出来的,可以直接读写 ArrayBuffer 的数据。

观察下面的代码:

上面的代码会输出 [8250]。这个结果是因为在这个例子中新建了一个 ArrayBuffer 和其 DataView。这个 DataView 的字节偏移量是从 TextView 开始的。Int16Array 会在 TextView 内的数据上完成位序列从低位到高位的合成。

2.2 Buffer

Node.js 中的 Buffer 是对 ArrayBuffer 的再次包裹。我们可以通过使用如下代码创建一个 Buffer:

注意,这个版本的 Buffer 是有一些安全风险的,因为它默认会对内存池进行可写可读的授权。具有可执行代码的 JPEG 或 PNG 的 header,如果在其中寄有一个故障,就有可能越过 Boundary 覆盖一些开了 W^X 属性的内存区塊,从而控制计算机的权限。

这个安全风险只出现在推荐的转码方式汇总,其中会在含有大量内存分配的场景下失控,比如 zlib 初始化阶段中的大量内存分配等。

在 Node.js v8.3 以后的版本中,Buffer 的创建方式推荐是:

这个方式是通过系统程序来初始化内存池,如果系统程序逊于 v8.3 或者没有可用的内存池,系统程序在调用 gc 的时候也会主动去通知系统释放规格超出大小阈值的 Buffer,更加健壮。使用 Buffer.alloc 返回的对象同样可以像 ArrayBuffer 一样被使用。

2.3 区别与应用场景

使用 ArrayBuffer 能够做的事情,使用 Buffer 和 TypedArray 也能够做到,反之亦然,区别在于它们的应用场景和性能上的差异。下面我们来探讨一下这两个不同类型之间的区别。

2.3.1 数组复制

Buffer 的特别之处是它可以很容易地对二进制数据进行读写,而无需进行任何的索引运算。Buffer 是在 EventEmitter 上以可读流和可写流的形式实现的,是 Node.js 标准库中的内置类型。它能够在将 Buffer 写到流中之前进行加工,比如: 添加压缩、加密。在读取流中的 Buffer 时能够进行相反的加工,比如解压、解密。 Buffer 类型本身也提供了很多的 API,这些 API 是一些方便使用和优化性能的小工具。

下面是一个使用 Buffer 的示例。这个代码会把同样的数据复制给三个新的 Buffer 对象。

2.3.2 Array 的行为

TypedArray 是通常在 GPU、硬件级别处理程序和高端音视频的引擎中才会用到的高级工具。它是对 Array 的补充,但二者有许多明显的差异。在 Array 上执行很多通用转换都是比较容易的,因为 Array 具有高度灵活性。

意味着很多从 TS 转换过来的遗产代码不需要太多的重构以支持 TypedArray。这时,我们可以使用 TypedArray 来避免因移动一段数据而带来的内存和时间成本。

下面我们使用 TypedArray 将数组乘以一个常数。

上面的代码会输出 [2,4,6,8]

总的来说,如果我们需要在标准的数组结构上再做一些有用的附加操作,这时候就要使用 TypedArray 来解决问题。

3. 总结

TypedArray 在处理二进制数据时能够发挥很大的作用,不过其原理和使用方法较为复杂。熟悉了 ES6 中 TypedArray 的相关知识后可以让我们在处理此类问题时更加得心应手。同时,我们能够针对实际问题判断何时使用 TypedArray 以及何时使用 ArrayBuffer 和 Buffer,这样能够更好地节省计算机资源和提高我们的代码运行效率。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/64f95a7ff6b2d6eab30e08fc

纠错
反馈