二进制数组

二进制数组

ArrayBuffer 对象、TypedArray 视图和 DataView 视图是 JavaScript 以数组的语法操作二进制数据接口,也叫二进制数组;这些接口设计的初衷是为了满足浏览器与显卡通信时大量实时的数据交换

二进制数组并非真正的数组,而是类数组对象

ES6 将二进制数组纳入规范,并增加了新的方法

视图

JavaScript 类型数组(Typed Arrays)将实现拆分为缓冲和视图两部分。一个缓冲(由 ArrayBuffer 对象实现)描述的是一个数据块。缓冲没有格式且不提供机制访问其内容;

为了访问缓冲对象中包含的内存,需要使用视图;视图提供了上下文 — 即数据类型、起始偏移量和元素数 — 将数据转换为实际有类型的数组

类型数组包含两种视图,一种是 TypedArray 视图,另一种是 DataView 视图;前者的数组成员都是同一个数据类型,后者的数组成员可以是不同的数据类型

ArrayBuffer

ArrayBuffer 是一种数据类型,用来表示一个通用的、固定长度的二进制数据缓冲区;这段缓冲区内容无法直接读写,只能通过 TypedArrayDataView 以指定格式解读这段二进制数据

ArrayBuffer 也是构造函数,可以分配一段存储数据的连续内存区域;构造函数的参数代表的是所需内存大小(单位为字节),每个字节默认值均为 0

1
const buffer = new ArrayBuffer(32);

方法和属性

  • byteLength

    返回所分配的内存区域的字节长度

    需要检查是否分配成功;如果待分配的内存区域过大,有可能分配失败(没有足够多的连续空余内存)

    1
    2
    3
    4
    5
    6
    7
    8
    const buffer = new ArrayBuffer(32);
    buffer.byteLength; // 32

    if (buffer.byteLength === n) {
    // 分配成功
    } else {
    // 分配失败
    }
  • slice()

    截取原 ArrayBuffer 对象,生成一份新的 ArrayBuffer 对象

    该方法参数与数组 slice 一致,第一个参数指截取开始位置,第二个参数指的是截取结束位置(不包含)

    1
    2
    const buffer = new ArrayBuffer(32);
    const newBuffer = buffer.slice(0, 3);
  • ArrayBuffer.isView()

    此方法为 ArrayBuffer 静态方法,判断参数是否为 TypedArrayDataView 实例

    1
    2
    3
    4
    5
    const buffer = new ArrayBuffer(32);
    ArrayBuffer.isView(buffer);

    const v = new Int32Array(buffer);
    ArrayBuffer.isView(v);

TypedArray

TypedArray 包括 9 种类型,每一种类型都是一种构造函数

类型 描述 取值范围 长度
Int8Array 8 位有符号整数 -128 ~ 127 1
Uint8Array 8 位无符号整数 0 ~ 255 1
Uint8ClampedArray 8 位无符号整数(溢出处理不同) 0 ~ 255 1
Int16Array 16 位有符号整数 -32768 ~ 32767 2
Uint16Array 16 位无符号整数 0 ~ 65535 2
Int32Array 32 位有符号整数 -2147483638 ~ 213647483647 4
Uint32Array 32 位无符号整数 0 ~ 4294967295 4
Float32Array 32 位浮点数 1.2x10-38 ~ 3.4x1038 4
Float64Array 64 位浮点数 5.0x10-324 ~ 1.8x10308 8

普通数组与 TypedArray 数组差异

  • TypedArray 数组的所有成员都是同一种类型
  • TypedArray 数组的成员是连续的
  • TypedArray 数组成员的默认值为 0
  • TypedArray 数组只是一层视图,本身不储存数据,数据都储存在底层 ArrayBuffer 对象之中
  • TypedArray 数组包含的方法与普通数组基本一致,唯一特殊的是没有 concat 方法

构造函数

TypedArray 数组提供 9 种构造函数,用来生成相应类型的数组实例。
下面是构造函数的各种用法:

  • TypedArray(buffer, byteOffset=0, length?)

    第一个参数为必需,指 ArrayBuffer 对象,后面两个参数分别是开始和结束位置,默认从 0 开始到最大长度

    根据同一 ArrayBuffer 对象创建多个 TypedArray 时,对任何一个类型数组进行修改,都会影响其它类型数组,因为类型数组只是视图

    创建不同类型的类型数组时,byteOffset 参数有相应的字节倍数限制,如:Int16Array 类型数组是 4 字节,因而 byteOffset 只能为 4 的倍数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const buffer = new ArrayBuffer(8);

    const buff_16 = new Uint16Array(buffer);
    const buff_32 = new Int32Array(buffer);

    buff_16[0] = 10;
    buff_16[1] = 11;

    console.log(buff_16);
    console.log(buff_32);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    const buffer = new ArrayBuffer(8);

    console.log(new Int8Array(buffer, 1)); // ​​​​​Int8Array { [Iterator] '0': 0, '1': 0, '2': 0, '3': 0, '4': 0, '5': 0, '6': 0 }​​​​​

    new Int16Array(buffer, 1); // ​​start offset of Int16Array should be a multiple of 2​​
    console.log(new Int16Array(buffer, 2)); // ​​​​​Int16Array { [Iterator] '0': 0, '1': 0, '2': 0 }​​​​​

    new Int32Array(buffer, 2); ​​// start offset of Int32Array should be a multiple of 4​​
    console.log(new Int32Array(buffer, 4)); // ​​​​​Int32Array { [Iterator] '0': 0 }​​​​​

    new Float32Array(buffer, 2); ​​// ​​start offset of Float32Array should be a multiple of 4​​
    console.log(new Float32Array(buffer, 4)); // ​​​​​Float32Array { [Iterator] '0': 0 }​​​​​

    new Float64Array(buffer, 4); // ​​start offset of Float64Array should be a multiple of 8​​
    console.log(new Float64Array(buffer)); // ​​​​​Float64Array { [Iterator] '0': 0 }​​​​​
    console.log(new Float64Array(buffer, 8)); ​​​​​// Float64Array { [Iterator] }​​​​​
  • TypedArray(length)

    也可以不通过 ArrayBuffer 对象,直接分配内存长度,再进行赋值

    1
    2
    3
    4
    5
    6
    const buff_8 = new Int8Array(8);

    buff_8[0] = 1;
    buff_8[1] = 2;

    console.log(buff_8);

  • TypedArray(typedArray)

    还可以接受另外一个 TypedArray 实例作为参数,生成新的 TypedArray 类型数组

    新生成的 TypedArray 数组只是复制了原数组的值,对应底层 ArrayBuffer 不一样;新数组生成时也会创建新的 ArrayBuffer 对象存储数据,不会在原数组的 ArrayBuffer 对象上建立视图

    1
    2
    3
    4
    5
    6
    7
    8
    const buff_8 = new Int8Array(2);
    const buff8 = new Int8Array(buff_8);

    buff_8[0] = 1;
    buff8[0] = 2;

    console.log(buff_8);
    console.log(buff8);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 基于同一 ArrayBuffer 建立视图
    const buff_8 = new Int8Array([1, 2]);
    const buff8 = new Int8Array(buff_8.buffer);

    console.log(buff_8[0]);
    console.log(buff8[0]);

    buff_8[0] = 2;
    console.log(buff8[0]);

  • TypedArray(array)

    构造函数的参数也可以是一个普通数组或类数组,然后直接生成 TypedArray 实例

    此时 TypedArray 视图不会在原数组的内存上建立视图,会重新创建 ArrayBuffer 对象

    1
    2
    3
    4
    5
    const buff_8 = new Int8Array([1, 2, 3]);
    console.log(buff_8);

    const buff_16 = new Int16Array({ length: 2, 0: 0, 1: 2});
    console.log(buff_16);