搞懂 ArrayBuffer、TypedArray、DataView 的对比和使用
一张图说明
为了说明他们之间的关系,我画了一张图,其中箭头不代表包含关系,是底层到上层的关系,我们在后面会分别说明一下
ArrayBuffer
ArrayBuffer 对象代表存储二进制数据的一段内存,是一个字节数组。
它不能被直接读写,需要创建视图来对它进行操作,视图可以以指定格式操作二进制数据。
ArrayBuffer 也是一个构造函数,可以通过它创建连续的内存区域,参数是内存大小(单位字节),默认初始值都是 0
视图
ArrayBuffer 需要通过视图进行操作,视图会有两种形式:类型化数组(TypedArray) 或者 DataView,TypeArray 读写 11 种特定类型的二进制数据,DataView 用来读写自定义复合类型的二进制数据
我们在分别介绍一下
类型化数组(TypedArray)
TypeArray 是 ArrayBuffer 的一个二进制数据的操作视图,实际上,没有名为 TypeArray 的全局变量,也没有名为 TypeArray 的构造函数,我认为可以把它理解为一个 11 种特定类型视图的集合,是一个统称。
这 11 种特定类型视图可以参考 MDN:
他们分别都是构造函数,通过这些构造函数生成的数组,和普通数组类似,可以使用所有数组的方法,它们和普通数组的区别是:
TypeArray 元素都是一个类型的
TypeArray 元素是连续的,不会有空位
TypeArray 模式元素的初始值都是 0
TypeArray 只是一层视图,数据都存储到底层的 ArrayBuffer 中
那么,如何使用这些视图来对 ArrayBuffer 进行操作呢?
下面是各种 TypeArray 构造函数的用法举例,注意这里只是语法格式,实际使用时需要替换为各种构造函数。
如果我们在 ArrayBuffer 上新建视图的话
这是我们可以看到,使用 int8 和 int16 两种方式新建的视图是相互影响的,都是直接修改的底层 buffer 的数据,他们只是操作底层 buffer 数据的两种视图。
另外,普通数组的操作方法,对 TypeArray 也完全适用。和普通数组相比 TypeArray 的最大优势是可以直接操作内存,另外,不需要做数据类型的转换,于是速度要快很多。
DataView
如果一段数据中包括多种类型(比如从服务器传来的 http 数据),除了可以通过分别设置 TypeArray 起始字节和长度外,还可以使用 DataView 来做自定义的复合视图。
在初始设计上 ArrayBuffer 的 TypedArray 视图,是用来向网卡、声卡等本机设备传递数据;DataView 是用来处理网络设备传来的数据的,并且支持设置字节序(将在后面讲到)
DataView 本身也是一个构造函数
举个例子:
这样就新建了一个 DataView 视图,DataView 实例提供了 10 种方法读写内存
以 DataView.prototype.getInt8()
为例
表示 DataView 实例从第 byteOffset 字节开始,读取一个有符号的 8bit 整数(一个字节),继续前面的举个例子:
这就是利用实例上的不同方法,进行内存读取和写入的操作
到这里你可能认为,如果通过不同类型的 TypeArray,指定起始字节和长度,也能达到一样的效果,嗯现在看是的,但是 DataView 还有另外一个特性:设置字节序,我们进到下一章看一下
字节序
什么是字节序
首先说一下什么是字节序,字节序是数值在内存中的存储方式。分为小端字节序(little-endian)和大端字节序(big-endian)两种
所有的英特尔处理器都使用小端字节序,我们个人电脑基本都是小端字节序,小端字节序会把最不重要的放在最前,可类比欧洲通用的日期书写方式(例如,31 December 2050。年份是最重要的,月份其次,日期最后)
大端字节序则是相反的顺序,可类比 ISO 日期格式(例如 2050-12-31)。big-endian 通常被称作"网络字节顺序"("network byte order"), 因为互联网标准通常要求数据使用 big-endian 存储,从标准 Unix 套接字(socket)层开始,一直到标准化网络的二进制数据结构。
字节序和 TypedArray、DataView 的关系
TypedArray 中,字节序会跟随系统的字节序,于是基本都是小端字节序,是不支持自己设置的,于是就会带来一个问题:如果从网络请求来的数据是大端字节序,会导致数据无法解析。
相比之下,DataView 可以支持设置字节序,举个例子:
DataView 实例方法的第二个参数,可以用来设置字节序,默认是大端字节序
如果不确定计算机上的字节序,可以通过这个方法来判断:
如果返回true
,就是小端字节序;如果返回false
,就是大端字节序。
这部分具体可以参考:https://es6.ruanyifeng.com/#docs/arraybuffer#DataView-%E8%A7%86%E5%9B%BE
参考
https://es6.ruanyifeng.com/#docs/arraybuffer#ArrayBuffer-%E5%AF%B9%E8%B1%A1
ArrayBuffer:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
TypedArray:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
字节序:https://developer.mozilla.org/zh-CN/docs/Glossary/Endianness
版权声明: 本文为 InfoQ 作者【每天进步亿点点】的原创文章。
原文链接:【http://xie.infoq.cn/article/66cf41a151aadbf8dcae7c8b5】。文章转载请联系作者。
评论