前言
全文摘抄自阮一峰的<>,单纯作为笔记使用
ArrayBuffer
Arraybuffer对象, TypedArray视图和DataView视图是Javascript 操作二进制数据的一个接口它们都是以数组的语法处理二进制数据,所以统称为二进制数组.
这个接口原始设计目的与WebGL项目有关,即浏览器与显卡之间的通信接口,为了满足Javascript与显卡1大量的,实时的数据交换,它们之间的通信必须是二进制的,而不能是传统的文本格式(文本格式传递一个32位整数,两端都要进行格式转换将非常耗时)
二进制数组组成
ArrayBuffer对象
代表内存之中的一段二进制数据,可以通过"视图"进行操作,"视图"部署了数组接口,意味着可以用数组方法操作内存
TypedArray视图
共9种类型视图,下面会提到
DataView视图
可以自定义复合格式的视图,自定义字节序
总结
ArrayBuffer: 代表原始的二进制数据
TypedArray: 读写简单类型的二进制数据
DataView: 读写复杂类型的二进制数据
ArrayBuffer对象
ArrayBuffer也是一个构造函数,可以分配一段可以存放数据的连续内存区域。
const buf = new ArrayBuffer(32);
上面代码生成了一段 32 字节的内存区域,每个字节的值默认都是 0。可以看到,ArrayBuffer构造函数的参数是所需要的内存大小(单位字节)。
DataView
为了读写这段内容,需要为它指定视图。DataView视图的创建,需要提供ArrayBuffer对象实例作为参数。
const dataView = new DataView(buf);
dataView.getUint8(0) // 0
上面代码对一段 32 字节的内存,建立DataView视图,然后以不带符号的 8 位整数格式,从头读取 8 位二进制数据,结果得到 0,因为原始内存的ArrayBuffer对象,默认所有位都是 0
TypedArray
与DataView视图的一个区别是,它不是一个构造函数,而是一组构造函数,代表不同的数据格式。
const buffer = new ArrayBuffer(12);
const x1 = new Int32Array(buffer);
x1[0] = 1;
const x2 = new Uint8Array(buffer);
x2[0] = 2;
x1[0] // 2
上面代码对同一段内存,分别建立两种视图:32 位带符号整数(Int32Array构造函数)和 8 位不带符号整数(Uint8Array构造函数)。由于两个视图对应的是同一段内存,一个视图修改底层内存,会影响到另一个视图。
TypedArray视图的构造函数,除了接受ArrayBuffer实例作为参数,还可以接受普通数组作为参数,直接分配内存生成底层的ArrayBuffer实例,并同时完成对这段内存的赋值。
const typedArray = new Uint8Array([0,1,2]);
typedArray.length // 3
typedArray[0] = 5;
typedArray // [5, 1, 2]
实例属性方法
ArrayBuffer.prototype.byteLength: 返回所分配的内存区域的字节长度
ArrayBuffer.prototype.slice(): 允许将内存区域的一部分,拷贝生成一个新的ArrayBuffer对象
ArrayBuffer.isView(): 表示参数是否为ArrayBuffer的视图实例(TypedArray实例或DataView实例)
TypedArray 视图
与普通数组类似,差异在于:
TypedArray 数组的所有成员,都是同一种类型。
TypedArray 数组的成员是连续的,不会有空位。
TypedArray 数组成员的默认值为 0。
TypedArray 数组只是一层视图,本身不储存数据,它的数据都储存在底层的ArrayBuffer对象之中,要获取底层对象必须使用buffer属性。
共9种视图类型
数据类型
字节长度
含义
对应的 C 语言类型
Int8
1
8 位带符号整数
signed char
Uint8
1
8 位不带符号整数
unsigned char
Uint8C
1
8 位不带符号整数(自动过滤溢出)
unsigned char
Int16
2
16 位带符号整数
short
Uint16
2
16 位不带符号整数
unsigned short
Int32
4
32 位带符号整数
int
Uint32
4
32 位不带符号的整数
unsigned int
Float32
4
32 位浮点数
float
Float64
8
64 位浮点数
double
构造函数有多种用法
(1)TypedArray(buffer, byteOffset=0, length?)
第一个参数(必需):视图对应的底层ArrayBuffer对象。
第二个参数(可选):视图开始的字节序号,默认从 0 开始。
第三个参数(可选):视图包含的数据个数,默认直到本段内存区域结束。
// 创建一个8字节的ArrayBuffer
const b = new ArrayBuffer(8);
// 创建一个指向b的Int32视图,开始于字节0,直到缓冲区的末尾
const v1 = new Int32Array(b);
// 创建一个指向b的Uint8视图,开始于字节2,直到缓冲区的末尾
const v2 = new Uint8Array(b, 2);
// 创建一个指向b的Int16视图,开始于字节2,长度为2
const v3 = new Int16Array(b, 2, 2);
注意,byteOffset必须与所要建立的数据类型一致,否则会报错。
const buffer = new ArrayBuffer(8);
const i16 = new Int16Array(buffer, 1);
// Uncaught RangeError: start offset of Int16Array should be a multiple of 2
(2)TypedArray(length)
不通过ArrayBuffer对象,直接分配内存而生成
const f64a = new Float64Array(8);
f64a[0] = 10;
f64a[1] = 20;
f64a[2] = f64a[0] + f64a[1];
(3)TypedArray(typedArray)
接受另一个TypedArray实例作为参数, 此时生成的新数组,只是复制了参数数组的值,对应的底层内存是不一样的。新数组会开辟一段新的内存储存数据,不会在原数组的内存之上建立视图。
const x = new Int8Array([1, 1]);
const y = new Int8Array(x);
x[0] // 1
y[0] // 1
x[0] = 2;
y[0] // 1
如果想基于同一段内存,构造不同的视图,可以采用下面的写法
const x = new Int8Array([1, 1]);
const y = new Int8Array(x.buffer);
x[0] // 1
y[0] // 1
x[0] = 2;
y[0] // 2
(4)TypedArray(arrayLikeObject)
参数是一个普通数组直接生成TypedArray实例, 这时TypedArray视图会重新开辟内存,不会在原数组的内存上建立视图
const typedArray = new Uint8Array([1, 2, 3, 4]);
TypedArray 数组也可以转换回普通数组
const normalArray = [...typedArray];
// or
const normalArray = Array.from(typedArray);
// or
const normalArray = Array.prototype.slice.call(typedArray);
数组方法
普通数组的操作方法和属性,对 TypedArray 数组完全适用
TypedArray.prototype.copyWithin(target, start[, end = this.length])
TypedArray.prototype.entries()
TypedArray.prototype.every(callbackfn, thisArg?)
TypedArray.prototype.fill(value, start=0, end=this.length)
TypedArray.prototype.filter(callbackfn, thisArg?)
TypedArray.prototype.find(predicate, thisArg?)
TypedArray.prototype.findIndex(predicate, thisArg?)
TypedArray.prototype.forEach(callbackfn, thisArg?)
TypedArray.prototype.indexOf(searchElement, fromIndex=0)
TypedArray.prototype.join(separator)
TypedArray.prototype.keys()
TypedArray.prototype.lastIndexOf(searchElement, fromIndex?)
TypedArray.prototype.ma