1. 什么是定型数组?
定性数组是js新增的一种数据结构,目的是提升向原生库传输数据的效率。
2. 历史
随着浏览器的越来越强大,人们开始期待浏览器能用来运行复杂的3d应用程序,以充分利用计算机的3d图形API和GPU加速器。于是有了WebGL(Web Graph Library),在早起的WebGL版本中,javascript数组与原生数组不匹配,所以出现了性能问题。这里的不匹配主要是指,js数组的数值默认是双精度浮点格式,但这不是WebGL图形驱动API所需要的,所以在这二者之间传递数组时,需要在WebGL运行环境中分配一个新数组,然后迭代js数组并且进行转化。如果数组长度很长,这将花费很多时间。javascript能不能有一种数组,当把这个数组传递给WebGL时,WebGL能够直接使用呢?答案就是使用定型数组。
3. ArrayBuffer
在将定型数组之间需要知道ArrayBuffer,从字面意义来看,它好像是一种数组缓冲区?答案是,ArrayBuffer是一块预分配内存,其实就是一块内存,这块内存js可以访问,但是不能直接访问,要使用一定的工具,这个工具就是视图。视图包括DataView, Float32Array等等。Float32Array就是定型数组。
const buf = new ArrayBuffer(3) //通过ArrayBuffer这个js普通的构造函数来创建一个指定字节长度的内存空间。
console.log(buf.byteLength); //查看这个分配的这块内存空间的字节长度
ArrayBuffer一经创建就不能再调整内存空间的大小,但是可以通过切片来复制全部或者部分到另一个新ArrayBuffer实例。垃圾回收机制能够自动回收被分配的内存,不需要手动释放。
const buf = new ArrayBuffer(16)
console.log(buf.byteLength);
const buf2 = buf.slice(0,8) //通过切片复制
console.log(buf2.byteLength);
4. DataView
之前有提到,虽然能创建buf,但是不能仅仅通过buf来读或者写这一块内存空间,需要使用视图,DataView就是这样的视图。DataView 的API提供了对ArrayBuffer的高度控制,但是性能比其他的视图弱。
创建DataView的前提是已经创建好了ArrayBuffer实例。
const buf = new ArrayBuffer(16)
//new DataView(buffer:ArrayBufferLike, byteOffeset:number,byteLength: number)
const fullDataView = new DataView(buf,1, 8)
console.log(fullDataView.buffer);
console.log(fullDataView.byteLength);
console.log(fullDataView.byteOffset);
通过DataView读写缓冲区还需要:
- 要读或者写的字节偏移量。可以看成是DataView中的某种地址。
- 指定ElementType来完成js NUmber类型到缓冲区二进制格式的转换
- 内存中的字节序,默认为大端字节序
ElementType:
Int 8, 16,32 有符合整数,比如In8,8位为一个字节,Int8为一个字节。
Uint 8,16,32 无符号整数
Float32 4字节32为浮点数
Float64 8字节64位浮点数
每一种类型都暴露了set 和 get方法。
5. 定型数组
他是另一种形式的ArrayBuffer视图,提供了适用面更广的API和更高的性能。
a. 创建定型数组:
- 读取已有的缓冲区
- 使用自有缓冲区
- 填充可迭代结构
- 填充基于任意类型的定性数组
- <ElementType>.form() 和<ElementType>.of()
// 创建一个12 字节的缓冲
const buf = new ArrayBuffer(12);
// 创建一个引用该缓冲的Int32Array
const ints = new Int32Array(buf);
// 这个定型数组知道自己的每个元素需要4 字节
// 因此长度为3
alert(ints.length); // 3
// 创建一个长度为6 的Int32Array
const ints2 = new Int32Array(6);
// 每个数值使用4 字节,因此ArrayBuffer 是24 字节
alert(ints2.length); // 6
// 类似DataView,定型数组也有一个指向关联缓冲的引用
alert(ints2.buffer.byteLength); // 24
// 创建一个包含[2, 4, 6, 8]的Int32Array
const ints3 = new Int32Array([2, 4, 6, 8]);
alert(ints3.length); // 4
alert(ints3.buffer.byteLength); // 16
alert(ints3[2]); // 6
// 通过复制ints3 的值创建一个Int16Array
const ints4 = new Int16Array(ints3);
// 这个新类型数组会分配自己的缓冲
// 对应索引的每个值会相应地转换为新格式
alert(ints4.length); // 4
alert(ints4.buffer.byteLength); // 8
alert(ints4[2]); // 6
// 基于普通数组来创建一个Int16Array
const ints5 = Int16Array.from([3, 5, 7, 9]);
alert(ints5.length); // 4
alert(ints5.buffer.byteLength); // 8
alert(ints5[2]); // 7
// 基于传入的参数创建一个Float32Array
const floats = Float32Array.of(3.14, 2.718, 1.618);
alert(floats.length); // 3
alert(floats.buffer.byteLength); // 12
alert(floats[2]); // 1.6180000305175781
b. 定型数组可以使用的数组方法
定型数组同样使用缓冲区ArrayBuffer来存储数据,而且缓冲区无法调整大小,因此会改变数组长度的方法均不适用定型数组。
- concat()
- pop()
- push()
- shift()
- splice()
- unshift()
其他的所有数组方法均可适用于定型数组。
c. 定性数组自己的方法
① set()
从提供的数组或者定型数组中把值复制到当前定型中指定的位置
② subarray()
和set相反,相当于是截取并返回一个指定范围的新定型数组