写点什么

搞懂 ArrayBuffer、TypedArray、DataView 的对比和使用

发布于: 2 小时前

一张图说明

为了说明他们之间的关系,我画了一张图,其中箭头不代表包含关系,是底层到上层的关系,我们在后面会分别说明一下


ArrayBuffer

ArrayBuffer 对象代表存储二进制数据的一段内存,是一个字节数组。


它不能被直接读写,需要创建视图来对它进行操作,视图可以以指定格式操作二进制数据。


ArrayBuffer 也是一个构造函数,可以通过它创建连续的内存区域,参数是内存大小(单位字节),默认初始值都是 0


const buf = new ArrayBuffer(32)
复制代码

视图

ArrayBuffer 需要通过视图进行操作,视图会有两种形式:类型化数组(TypedArray) 或者 DataView,TypeArray 读写 11 种特定类型的二进制数据,DataView 用来读写自定义复合类型的二进制数据


我们在分别介绍一下

类型化数组(TypedArray)

TypeArray 是 ArrayBuffer 的一个二进制数据的操作视图,实际上,没有名为 TypeArray 的全局变量,也没有名为 TypeArray 的构造函数,我认为可以把它理解为一个 11 种特定类型视图的集合,是一个统称。


这 11 种特定类型视图可以参考 MDN


他们分别都是构造函数,通过这些构造函数生成的数组,和普通数组类似,可以使用所有数组的方法,它们和普通数组的区别是:


  1. TypeArray 元素都是一个类型的

  2. TypeArray 元素是连续的,不会有空位

  3. TypeArray 模式元素的初始值都是 0

  4. TypeArray 只是一层视图,数据都存储到底层的 ArrayBuffer 中


那么,如何使用这些视图来对 ArrayBuffer 进行操作呢?


const typeArray = new Int8Array(8);typeArray[0] = 32;console.log(typeArray);  // [32, 0, 0, 0, 0, 0, 0, 0]
const typeArray1 = new Int8Array(typeArray);typeArray1[1] = 42;console.log(typeArray1); // [32, 42, 0, 0, 0, 0, 0, 0]
复制代码


下面是各种 TypeArray 构造函数的用法举例,注意这里只是语法格式,实际使用时需要替换为各种构造函数。


// 下面代码是语法格式,不能直接运行,// TypedArray 关键字需要替换为各种构造函数。new TypedArray(); // ES2017中新增new TypedArray(length);new TypedArray(typedArray);new TypedArray(object);new TypedArray(buffer [, byteOffset [, length]]);
复制代码


如果我们在 ArrayBuffer 上新建视图的话


// 生成8个字节内存空间const buf = new ArrayBuffer(8);const int8Array = new Int8Array(buf, 0);int8Array[3] = 32;const int16Array = new Int16Array(buf, 0, 4);int16Array[0] = 42;console.log(int16Array);  // [42, 8192, 0, 0] 因为第三个字节被设置为 32,于是使用两个字节表示一个整数时,就变成了 00010000 00000000,也就是 2 的 13 次方,8192console.log(int8Array);   // [42, 0, 0, 32, 0, 0, 0, 0]
复制代码


这是我们可以看到,使用 int8 和 int16 两种方式新建的视图是相互影响的,都是直接修改的底层 buffer 的数据,他们只是操作底层 buffer 数据的两种视图。


另外,普通数组的操作方法,对 TypeArray 也完全适用。和普通数组相比 TypeArray 的最大优势是可以直接操作内存,另外,不需要做数据类型的转换,于是速度要快很多。

DataView

如果一段数据中包括多种类型(比如从服务器传来的 http 数据),除了可以通过分别设置 TypeArray 起始字节和长度外,还可以使用 DataView 来做自定义的复合视图。


在初始设计上 ArrayBuffer 的 TypedArray 视图,是用来向网卡、声卡等本机设备传递数据;DataView 是用来处理网络设备传来的数据的,并且支持设置字节序(将在后面讲到)


DataView 本身也是一个构造函数


new DataView(ArrayBuffer buffer [, 字节起始位置 [, 长度]]);
复制代码


举个例子:


const buf = new ArrayBuffer(24);const dataview = new DataView(buf);
复制代码


这样就新建了一个 DataView 视图,DataView 实例提供了 10 种方法读写内存



具体可以查看 MDN:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView#instance_methods


DataView.prototype.getInt8() 为例


dataview.getInt8(byteOffset)
复制代码


表示 DataView 实例从第 byteOffset 字节开始,读取一个有符号的 8bit 整数(一个字节),继续前面的举个例子:


const buf = new ArrayBuffer(24);const dataview = new DataView(buf);
dataview.setInt8(1, 3);dataview.getInt8(1); // 3
复制代码


这就是利用实例上的不同方法,进行内存读取和写入的操作


到这里你可能认为,如果通过不同类型的 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 可以支持设置字节序,举个例子:


const buffer = new ArrayBuffer(24);const dv = new DataView(buffer);
// 小端字节序const v1 = dv.getUint16(1, true);
// 大端字节序const v2 = dv.getUint16(3, false);
// 大端字节序const v3 = dv.getUint16(3);
复制代码


DataView 实例方法的第二个参数,可以用来设置字节序,默认是大端字节序


如果不确定计算机上的字节序,可以通过这个方法来判断:


const littleEndian = (function() {  const buffer = new ArrayBuffer(2);  new DataView(buffer).setInt16(0, 256, true);  return new Int16Array(buffer)[0] === 256;})();
复制代码


如果返回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

发布于: 2 小时前阅读数: 3
用户头像

还未添加个人签名 2019.01.15 加入

还未添加个人简介

评论

发布
暂无评论
搞懂 ArrayBuffer、TypedArray、DataView 的对比和使用