webGL-类型化数组
什么是类型化数组?
类型化数组是一个种特殊数据类型的数组,可在内存中存储和操作二进制数据。
类型化数组与常规数组(Array)的区别、性能对比。
- 连续的内存:类型化数组是一个连续的内存,这使得在内存中操作数组时会更高效,因为它们充分的利用了硬件的缓存机制。常规数组的内存是分散的,需要更多的内存操作
- 高效的垃圾回收:类型化数组因为是一个类型固定的,连续内存数组,这使得在垃圾回收的表现更好。常规数组会频繁的对内存进行释放和开辟。比如:
- 如果对常规数组进行添加一个元素,因内存不够,可能触发对旧内存的释放,新内存的开辟,同时把旧内存中的元素拷贝到新内存中。
- 如果对常规数组进行删除元素,数组中的元素减少到一定量时,可能也会触发新旧内存的开辟与是否操作。
- 支持二进制数据的优势:类型化数组是直接操作的二进制数据,在处理大量数据的时候(比如对大量的图片、音频、视频)直接操作二进制数据会更加的高效。而常规数据需要对数据类型检查和解析工作后操作二进制数据,性能会很差。
常见的类型化数组
类型 | 值范围 | 字节大小 | 描述 | Web IDL 类型 | 等价的 C 类型 |
---|---|---|---|---|---|
Int8Array | -128 到 127 | 1 | 8 位有符号整型(补码) | byte |
int8_t |
Uint8Array | 0 到 255 | 1 | 8 位无符号整型 | octet |
uint8_t |
Uint8ClampedArray | 0 到 255 | 1 | 8 位无符号整型(一定在 0 到 255 之间) | octet |
uint8_t |
Int16Array | -32768 到 32767 | 2 | 16 位有符号整型(补码) | short |
int16_t |
Uint16Array | 0 到 65535 | 2 | 16 位无符号整型 | unsigned short |
uint16_t |
Int32Array | -2147483648 到 2147483647 | 4 | 32 位有符号整型(补码) | long |
int32_t |
Uint32Array | 0 到 4294967295 | 4 | 32 位无符号整型 | unsigned long |
uint32_t |
Float32Array | -3.4E38 到 3.4E38 并且 1.2E-38 是最小的正数 |
4 | 32 位 IEEE 浮点数(7 位有效数字,例如 1.234567 ) |
unrestricted float |
float |
Float64Array | -1.8E308 到 1.8E308 并且 5E-324 是最小的正数 |
8 | 64 位 IEEE 浮点数(16 位有效数字,例如 1.23456789012345 ) |
unrestricted double |
double |
BigInt64Array | -263 到 263 - 1 | 8 | 64 位有符号整型(补码) | bigint |
int64_t (signed long long) |
BigUint64Array | 0 到 264 - 1 | 8 | 64 位无符号整型 | bigint |
uint64_t (unsigned long long) |
怎么去快速的记住与理解每个不同类型的类型化数组呢?
首先需要知道一个基本的知识点即:一个字节有8个二进制位,8个二进制位可表示256个数,如果是无符号的数 可以的数值范围是【0,255】。如果是有符合的数可表示的范围是【-128,127】(因为还有一个0,所以是127),第一个二进制位(最高位)则表示符号了,0表示正数、1表示是负数。
为什么只能表示256个数呢?因为一个二进制位只能是 0/1。8个二进制位即:11111111,它们存在的排列组合 2*2*2*2*2*2*2*2 即256种排列方式。
下面我们解释一下类型,例如:int32Array
int类型是包含有符合和无符号,既可以表示正负数。
32指的是二进制位个数,因1个字节有8个二进制位,32/8=4,所以表示4个字节。
int32 合起来表示Array中的元素是一个 int32的数,所以我们可以说数组中每个元素占32个二进制位。也可以说每个元素占4个字节。其元素大小范围,2的32次幂【0,pow(2,32)-1】,因是正负所以元素的真正范围是【-pow(0,16),pow(2,16)-1】。其他类型以此类推即可。
总的概括一下就是,前面的int、uint、float等都是数据类型,数字是二进制位的个数。组合起来就是元素的数值存储形式,元素是用几个二进制位进行存储的,同时也约束了元素的大小。这也导致在类型转换时,数据的丢失或覆盖情况。
类型化数组的创建与使用
/
API:
byteLength 获取数组的总字节长度
length 获取数组中元素的个数
BYTES_PER_ELEMENT 获取数组中每个元素的字节长度
*/
/*
buffer 创建。创建一个有8个字节的缓存 不可进行读写操作必须使用视图数组或data数组使用
总共二进制位的个数:8*8=64 (一个字节有8个二进制位)
/
const buffer = new ArrayBuffer(4);
const int32View1 = new Int32Array(buffer);//格式化成 int32位,即取16个二进制位作为一个元素const int16View1 = new Int16Array(int32View1.buffer);//格式化成 int16位,即取16个二进制位作为一个元素
const uint8View1 = new Uint8Array(buffer);//格式化成 int8位,即取8个二进制位作为一个元素console.log(buffer.byteLength);//4
console.log(int32View1.byteLength, int32View1.length, int32View1.BYTES_PER_ELEMENT);//4 1 4
console.log(int16View1.byteLength, int16View1.length, int16View1.BYTES_PER_ELEMENT);//4 2 2
console.log(uint8View1.byteLength, uint8View1.length, uint8View1.BYTES_PER_ELEMENT);//4 4 1int32View1[0] = 255;
/* 以二进制形式输出视图数组中的元素* @param {视图数组} view * @param {每个元素有几个二进制位 binary count of per element} bCount */
function inputArrayBinary(view, bCount) {let strBinary = '';for (let i = 0; i < view.length; i++) {const b = view[i].toString(2).padStart(bCount, '0');strBinary += b + ' ';}console.log(strBinary);
}inputArrayBinary(int32View1, 32);//00000000000000000000000011111111
inputArrayBinary(int16View1, 16);//0000000011111111 0000000000000000
inputArrayBinary(uint8View1, 8);//11111111 00000000 00000000 00000000 console.log(int32View1)//[ 255 ]
console.log(int16View1)//[ 255, 0 ]
console.log(uint8View1)//[ 255, 0, 0, 0 ]console.log('----------偏移量测试-----------------');
/* 第一个参数是buffer* 第二个参数是偏移量,偏移量是相对于元素索引而言的* 第三个参数是从偏移量的索引位置开始(包含),往后总共的几个元素*/
uint8View1[2] = 99;
const uint8View2 = new Uint8Array(buffer, 1, 2);//先格式化为Uint8 类型的数组,然后从第1个元素开始,依次拿到2个元素作为该数组可操作的缓存,即16个二进制位。console.log(int16View1)//[ 255, 99 ]
console.log(uint8View1);//[ 255, 0, 99, 0 ]console.log(uint8View2);//[ 0, 99, 0 ]
console.log(uint8View2.byteLength, uint8View2.length, uint8View2.BYTES_PER_ELEMENT);//2 2 1console.log('----------数组创建-----------------');const arr = [1, 2];
const int32View2 = new Int32Array(arr); //用数组初始化,length即为数组的长度
console.log(int32View2);//[ 1, 2 ]
console.log(int32View2.byteLength, int32View2.length, int32View2.BYTES_PER_ELEMENT);//8 2 4console.log('----------数字创建-----------------');
const int32View3 = new Int32Array(3); //用数字初始化,传入的数字即为数组的长度,初始值为0
console.log(int32View3);//[ 0, 0, 0 ]
console.log(int32View3.byteLength, int32View3.length, int32View3.BYTES_PER_ELEMENT);//12 3 4
上面代码实例中,所有的类型化数组操作的都是同一个缓存,所以修改一个其他的均会被修改。
如果不是用buffer创建,则系统会自动创建一个 ArrayBuffer。
其他的API基本与常规数组(Array)一致。