javascript 高级编程系列 - 定型数组

定型数组是一种用于处理数值类型数据的专用数组,最早是在webGL中使用的,WebGL是OpenGL ES2.0的移植版, 在web页面中通过<canvas>元素来呈现它。定型数组也被一同移植而来,其可为javascript提供快速的按位运算。
在javascript中,数字是以64位浮点格式储存的,并按需转换为32位整数,所以算术运算非常慢,无法满足WebGL的需求,因此在ES6中引入定型数组来解决这个问题,并提供更高性能的算术运算。

所谓定型数组,就是将任何数字转换位一个包含数字比特的数组,随后就可以使用熟悉的数组方法进行处理。

1. 定型数组支持的数值数据类型

  • 有符号8位整数 (int8)
  • 无符号8位整数 (uint8)
  • 有符号的16位整数 (int16)
  • 无符号的16位整数 (uint16)
  • 有符号的32位整数 (int32)
  • 无符号的32位整数 (uint32)
  • 32位浮点数 (float32)
  • 64位浮点数 (float64)

2.数组缓冲区

数组缓冲区是所有定型数组的根基,它是一段可以包含特定数量字节的内存地址。创建数组缓冲区的过程
类似于在C语言中调用malloc()来分配内存,只是不需要指明内存块所包含的数据类型,通过构造函数
ArrayBuffer构造函数来创建数组缓冲区。

let buffer = new Arraybuffer(10); // 分配10字节
console.log(buffer.byteLength);   // 10字节
let buffer2 = buffer.slice(4, 6); // 创建从索引4和索引5提取的字节
console.log(buffer2.byteLength);  // 2

仅创建存储单元用途不大,除非能够将数据写到那个单元中,还需要创建一个视图来实现写入的功能。

3. 通过视图操作数组缓冲区

数组缓冲区是内存中的一段地址,视图是用来操作内存的接口。视图可以操作数组缓冲区或缓冲区字节的子集,
并按照其中一种数值型数据类型来读取和写入数据。DataView类型是一种通用的数组缓冲区视图,其支持所有
8种数值型数据类型。

  • 获取视图信息
    buffer 视图绑定的数组缓冲区
    byteOffset DataView构造函数的第二个参数,默认是0,只有传入参数时才有值。
    byteLength DataView构造函数的第三个参数,默认是缓冲区的长度byteLength
let buffer = new ArrayBuffer(10);
view1 = new DataView(buffer);  // 可以访问缓冲区中所有10字节
view2 = new DataView(buffer, 5, 2);  // 覆盖位于索引5和索引6的字节

console.log(view1.buffer === buffer);  // true
console.log(view2.buffer === buffer);  // true
console.log(view1.byteOffset);  // 0
console.log(view2.byteOffset);  // 5
console.log(view1.byteLength);  // 10
console.log(view2.byteLength);  // 2
  • 读取和写入数据
    javascript有8中数据类型,对于其中的每一种,都能在DataView的原型上找到相应的在数组缓冲区中写入数据
    和读取数据的方法。这些方法都以set或get打头,紧跟着的是每一种数据类型的缩写。

getInt8(byteOffset, littleEndian) 读取位于byteOffset后的int8类型数据。
setInt8(byteOffset, value, littleEndian) 在byteOffset处写入int8类型数据。
getUint8(byteOffset, littleEndian) 读取位于byteOffset后的uint8类型数据。
setUint8(byteOffset, value, littleEndian) 在byteOffset处写入uint8类型数据。
getFloat32(byteOffset, littleEndian) 读取位于byteOffset后的float32类型数据。
setFloat32(byteOffset, value, littleEndian) 在byteOffset处写入float32类型数据。
getFloat64(byteOffset, littleEndian) 读取位于byteOffset后的float64类型数据。
setFloat64(byteOffset, value, littleEndian) 在byteOffset处写入float64类型数据。

get方法接受两个参数:读取数据时偏移的字节数量和一个可选的布尔值,表示是否按照小端序进行读取。
set方法接受三个参数:写入数据时的偏移量,写入的值,和一个可选布尔值,表示是否按照小端序进行存储。

let buffer = new ArrayBuffer(2);
view = new DataView(buffer);

view.setInt8(0, 5);
view.setInt8(1, -1);
console.log(view.getInt8(0)); // 5
console.log(view.getInt8(1)); // -1

视图是独立的,无论数据之前是通过何种方式存储的,你都可在任意时刻读取或写入任意格式的数据。

console.log(view.getInt16(0));  // 1535
console.log(view.getInt8(0));  // 5

当混合使用不同数据类型时,DataView对象是一个完美的选择,然而如果只使用某个特定的数据类型,
那么特定类型的视图则是更好的选择。

4.创建特定类型的视图

ES6定型数组实际上是用于数组缓冲区的特定类型的视图,你可以强制使用特定数据类型,而不是使用通用
的DataView对象来操作数组缓冲区,8个特定类型的视图对应于8种数值型数据类型。

ES6中的特定类型视图

构造函数名称 元素尺寸 说明 等价的C语言类型
Int8Array 1 8位二进制补码有符号整数 有符号char类型
Uint8Array 1 8位无符号整数 无符号char类型
Unit8ClampedArray 1 8位无符号整数(强制转换) 无符号char类型
Int16Array 2 16位二进制补码有符号整数 有符号short类型
Uint16Array 2 16位无符号整数 无符号short类型
Int32Array 4 32位二进制补码有符号整数 有符号int类型
Uint32Array 4 32位无符号整数 无符号int类型
Float32Array 4 32位IEEE浮点数 float类型
Float64Array 8 64位IEEE浮点数 double类型
  • 创建定型数组的第一种方法:传入数组缓冲区,可选比特偏移量,可选的长度值
let buffer = new ArrayBuffer(10);
let view1 = new Int8Array(buffer);
let view2 = new Int8Array(buffer, 5, 2);
console.log(view1.buffer === buffer);   // true
console.log(view2.buffer === buffer);   // true
console.log(view1.byteOffset);  // 0
console.log(view2.byteOffset);  // 5
console.log(view1.byteLength);  // 10
console.log(view2.byteLength);  // 2
  • 创建定型数组的第二种方法: 调用构造函数时传入一个数字,这个数字表示分配给数组的元素数量(不是
    字节数量),构造函数将创建一个新的缓冲区,并按照数组元素的数量来分配合理的字节数量,通过length
    属性可以访问数组中的元素数量。
let ints = new Int16Array(2);
let floats = new Float32Array(5);
console.log(ints.byteLength); // 4
console.log(ints.length);  // 2
console.log(floats.byteLength); // 20
console.log(floats.length);  // 5
  • 创建定型数组的第三种方法: 调用构造函数时可以传入一个定型数组,一个可迭代对象,一个数组,
    一个类数组对象。
let ints1 = new Int16Array([25,50]);
let ints2 = new Int32Array(ints1);
console.log(ints1.buffer === ints2buffer);  // false
console.log(ints1.byteLength);  // 4
console.log(ints1[0]); // 25
console.log(ints1[1]); // 50
console.log(ints2.byteLength);  // 8
console.log(ints2.length); // 2
console.log(ints2[0]);  // 25
console.log(ints2[1]);  // 50
  • 定型数组的每个元素所占的字节数
console.log(Int8Array.BYTES_PER_ELEMENT);   // 1
console.log(Uint8Array.BYTES_PER_ELEMENT);  // 1
console.log(Int16Array.BYTES_PER_ELEMENT);  // 2
console.log(Uint16Array.BYTES_PER_ELEMENT); // 2
console.log(Int32Array.BYTES_PER_ELEMENT);  // 4
console.log(Uint32Array.BYTES_PER_ELEMENT); // 4
console.log(Float32Array.BYTES_PER_ELEMENT); // 4
console.log(Float64Array.BYTES_PER_ELEMENT); // 8

### 5. 定型数组的方法
- set() 将其他的数组复制到已有的定型数组
set()方法接受两个参数,一个是数组(定型数组或普通数组都支持),一个是可选的偏移量,表示开始插入
数据的位置,如果什么都不传,默认的偏移量为0

let ints = new Int16Array(4);
ints.set([25,50]);
ints.set([75, 100], 2);
console.log(ints.toString()); // 25, 50, 75, 100


- subarray() 提取已有定型数组的一部分作为一个新的定型数组
subarray()方法接受两个参数:一个是可选的开始位置,一个是可选的结束位置,最后返回一个新的定型数组

let ints = new Int16Array([25, 50, 75, 100]);
let subints1 = ints.subarray();
let subints2 = ints.subarray(2);
let subints3 = ints.subarray(1, 3);

console.log(subints1.toString()); // 25,50,75,100
console.log(subints2.toString()); // 75, 100
console.log(subints3.toString()); // 50, 75


- 不可使用的方法
concat(), shift(), unshift(), push(), pop(), splice()
因为这些方法都会改变数组的长度,定型数组的长度是只读属性,一旦确定无法改变,因此这些方法无法在
定型数组中使用。
posted @   箫笛  阅读(155)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示