1. 定型数组(typed array)是 ECMAScript 新增的结构,目的是提升向原生库传输数据的效率。实际上, JavaScript 并没有“TypedArray”类型,它所指的其实是一种特殊的包含数值类型的数组。

一、ArrayBuffer

Float32Array 实际上是一种“视图”,可以允许 JavaScript 运行时访问一块名为 ArrayBuffer 的 预分配内存。ArrayBuffer 是所有定型数组及视图引用的基本单位。

  1. ArrayBuffer()是一个普通的 JavaScript 构造函数,可用于在内存中分配特定数量的字节空间。
  2. const buf = new ArrayBuffer(16); // 在内存中分配 16 字节
  3. alert(buf.byteLength); // 16
  4. ArrayBuffer 一经创建就不能再调整大小。不过,可以使用 slice()复制其全部或部分到一个新
  5. 实例中:
  6. const buf1 = new ArrayBuffer(16);
  7. const buf2 = buf1.slice(4, 12);
  8. alert(buf2.byteLength); // 8
  1. ArrayBuffer()的特点
  2. 1ArrayBuffer 在分配失败时会抛出错误。
  3. 2ArrayBuffer分配的内存不能超过 Number.MAX_SAFE_INTEGER253 1)字节。
  4. 3、声明 ArrayBuffer 则会将所有二进制位初始化为 0
  5. 4、而通过声明ArrayBuffer 分配的堆内存可以被当成垃圾回收,不用手动释放。
  6. tips:不能仅通过对 ArrayBuffer 的引用就读取或写入其内容。要读取或写入 ArrayBuffer
  7. 就必须通过视图。视图有不同的类型,但引用的都是 ArrayBuffer 中存储的二进制数据。

二、 DataView

第一种允许你读写 ArrayBuffer 的视图是 DataView。这个视图专为文件 I/O 和网络 I/O 设计,其 API 支持对缓冲数据的高度控制,但相比于其他类型的视图性能也差一些。DataView 对缓冲内容没有 任何预设,也不能迭代。
必须在对已有的 ArrayBuffer 读取或写入时才能创建 DataView 实例。这个实例可以使用全部或 部分 ArrayBuffer,且维护着对该缓冲实例的引用,以及视图在缓冲中开始的位置。

  1. const buf = new ArrayBuffer(16);
  2. // DataView 默认使用整个 ArrayBuffer
  3. const fullDataView = new DataView(buf);
  4. alert(fullDataView.byteOffset); // 0
  5. alert(fullDataView.byteLength); // 16
  6. alert(fullDataView.buffer === buf); // true
  7. // 构造函数接收一个可选的字节偏移量和字节长度
  8. // byteOffset=0 表示视图从缓冲起点开始
  9. // byteLength=8 限制视图为前 8 个字节
  10. const firstHalfDataView = new DataView(buf, 0, 8);
  11. alert(firstHalfDataView.byteOffset); // 0
  12. alert(firstHalfDataView.byteLength); // 8
  13. alert(firstHalfDataView.buffer === buf); // true
  14. // 如果不指定,则 DataView 会使用剩余的缓冲
  15. // byteOffset=8 表示视图从缓冲的第 9 个字节开始
  16. // byteLength 未指定,默认为剩余缓冲
  17. const secondHalfDataView = new DataView(buf, 8);
  18. alert(secondHalfDataView.byteOffset); // 8
  19. alert(secondHalfDataView.byteLength); // 8
  20. alert(secondHalfDataView.buffer === buf); // true
  1. 要通过 DataView 读取缓冲,还需要几个组件。
  2. 首先是要读或写的字节偏移量。可以看成 DataView 中的某种“地址”。
  3. DataView 应该使用 ElementType 来实现 JavaScript Number 类型到缓冲内二进制格式的转
  4. 换。
  5. 最后是内存中值的字节序。默认为大端字节序。

三、ElementType

  1. ECMAScript 6 支持 8 种不同的 ElementType(见下表)。 <br />![elementtype.png](https://cdn.nlark.com/yuque/0/2021/png/21610487/1634128607945-0ff83e11-d959-49f3-9549-3e6c9d55594a.png#clientId=ud14b200c-56c5-4&from=ui&id=uf81537db&margin=%5Bobject%20Object%5D&name=elementtype.png&originHeight=423&originWidth=1318&originalType=binary&ratio=1&size=136718&status=done&style=none&taskId=u306e87bc-0b69-4424-aaa5-b5620e49f80)<br /> DataView 为上表中的每种类型都暴露了 get 和 set 方法,这些方法使用 byteOffset(字节偏移 量)定位要读取或写入值的位置。类型是可以互换使用的,如下例所示:
  1. // 在内存中分配两个字节并声明一个 DataView
  2. const buf = new ArrayBuffer(2);
  3. const view = new DataView(buf);
  4. // 说明整个缓冲确实所有二进制位都是 0
  5. // 检查第一个和第二个字符
  6. alert(view.getInt8(0)); // 0
  7. alert(view.getInt8(1)); // 0
  8. // 检查整个缓冲
  9. alert(view.getInt16(0)); // 0
  10. // 将整个缓冲都设置为 1
  11. // 255 的二进制表示是 11111111(2^8 - 1)
  12. view.setUint8(0, 255);
  13. // DataView 会自动将数据转换为特定的 ElementType
  14. // 255 的十六进制表示是 0xFF
  15. view.setUint8(1, 0xFF);
  16. // 现在,缓冲里都是 1 了
  17. // 如果把它当成二补数的有符号整数,则应该是-1
  18. alert(view.getInt16(0)); // -1

四、 字节序

“字节序”指的是计算系统维护的一种字节顺序的约 定。DataView 只支持两种约定:大端字节序和小端字节序。大端字节序也称为“网络字节序”,意思 是最高有效位保存在第一个字节,而最低有效位保存在最后一个字节。小端字节序正好相反,即最低有 效位保存在第一个字节,最高有效位保存在最后一个字节。
DataView 的所 有 API 方法都以大端字节序作为默认值,但接收一个可选的布尔值参数,设置为 true 即可启用小端 字节序。

  1. // 在内存中分配两个字节并声明一个 DataView
  2. const buf = new ArrayBuffer(2);
  3. const view = new DataView(buf);
  4. // 填充缓冲,让第一位和最后一位都是 1
  5. view.setUint8(0, 0x80); // 设置最左边的位等于 1
  6. view.setUint8(1, 0x01); // 设置最右边的位等于 1
  7. // 缓冲内容(为方便阅读,人为加了空格)
  8. // 0x8 0x0 0x0 0x1
  9. // 1000 0000 0000 0001
  10. // 按大端字节序读取 Uint16
  11. // 0x80 是高字节,0x01 是低字节
  12. // 0x8001 = 2^15 + 2^0 = 32768 + 1 = 32769
  13. alert(view.getUint16(0)); // 32769
  14. // 按小端字节序读取 Uint16
  15. // 0x01 是高字节,0x80 是低字节
  16. // 0x0180 = 2^8 + 2^7 = 256 + 128 = 384
  17. alert(view.getUint16(0, true)); // 384
  18. // 按大端字节序写入 Uint16
  19. view.setUint16(0, 0x0004);
  20. // 缓冲内容(为方便阅读,人为加了空格)
  21. // 0x0 0x0 0x0 0x4
  22. // 0000 0000 0000 0100
  23. alert(view.getUint8(0)); // 0
  24. alert(view.getUint8(1)); // 4
  25. // 按小端字节序写入 Uint16
  26. view.setUint16(0, 0x0002, true);
  27. // 缓冲内容(为方便阅读,人为加了空格)
  28. // 0x0 0x2 0x0 0x0
  29. // 0000 0010 0000 0000
  30. alert(view.getUint8(0)); // 2
  31. alert(view.getUint8(1)); // 0