类型化数组是 HTML5 中引入的API,它可以让开发者使用 JavaScript 直接操做二进制数据。在类型化数组出现以前,咱们是没法直接经过 JavaScript 操做二进制数据,一般都是操做 JavaScript 中的数据类型,由运行时转化成二进制。这就多了一个转化的过程,尽管 JavaScript 对数据类型作了不少优化以提升效率,但相比直接操做二进制来讲,仍然有效率上的差别。因而类型化数组就顺势推出了。canvas
那么,类型化数组的应用场景都有哪些呢?数组
那么,既然类型化数组这么重要,那还等什么,赶忙来掌握它们吧。浏览器
既然咱们要直接操做二进制数据,二进制数据又是存放在一段连续的内存区域中,因此咱们首先要有这么一段内存区域。bash
咱们能够建立一个内存区域:数据结构
let buffer = new ArrayBuffer()
复制代码
ArrayBuffer 是一个构造函数,容许咱们实例化数组缓冲区,数组缓冲区能够理解为是一段连续的内存区域。 因为咱们构造函数传入的参数是空,因此生成的 buffer 指向的内存长度是 0 字节,没有意义。函数
嗯,那咱们就建立一个有意义的内存区域。优化
buffer = new ArrayBuffer(8)
复制代码
咱们给ArrayBuffer 传入参数 8,意思是让浏览器帮咱们建立一段 8 个字节长度的内存区域。ui
咱们看下这段内存区域的长度是不是 8 个字节spa
console.log(buffer.byteLength);
复制代码
输出是 8, 看来浏览器没有欺骗咱们。3d
咱们猜测这 8 个字节里面的值应该都是 0 ,由于咱们并无给 buffer 赋值。让咱们确认一下吧,先看第一个字节:
console.log(buffer[0])
复制代码
输出: undefined。
咦?怎么是 undefined 呢?
哦,原来 buffer[0] 的意思是查看 buffer 这个对象 的属性为 0 的值,由于 buffer 没有 0 这个属性,因此是 undefined。
好吧,看来咱们查看 buffer 内容的姿式不对。
那该如何查看 buffer 内容呢?
珰珰珰珰,八大金刚闪亮登场~
- Int8Array:8 位有符号整数,长度 1 个字节。
- Uint8Array: 8位无符号整数, 1 个字节长度。
- Int16Array:16位有符号整数, 2 个字节长度。
- Uint16Array:16位无符号整数,2 个字节长度。
- Int32Array:32位有符号整数, 4 个字节长度。
- Uint32Array:32位无符号整数, 4 个字节长度。
- Float32Array:32位浮点数, 4 个字节长度。
- Float64Array:64位浮点数,8 个字节长度。
这八大金刚有什么神通呢?咱们没法直接读写 buffer 数据,而这八种数据类型充当了读写 buffer 内容的桥梁。
仍是上面的 buffer,咱们想查看一下 buffer 内容。首先建立一个读写该 buffer 的桥梁:
let int8Array = new Int8Array(buffer);
复制代码
咱们建立了一个读写 buffer 的桥梁,用 8 位有符号整数来读写 buffer。那如今咱们看看 buffer 第一位的内容是什么吧?
console.log(int8Array[0]);
复制代码
输出:0,看来初始化的时候,buffer 的各个字节存储的值默认都是 0 了。
咱们看看如何使用 Int8Array 给 buffer 赋值:
int8Array[0] = 30;
int8Array[1] = 41;
int8Array[2] = 52;
int8Array[3] = 63;
int8Array[4] = 74;
int8Array[5] = 85;
int8Array[6] = 86;
int8Array[7] = 97;
复制代码
很简单,由于 Int8Array 是一个字节的长度,和 buffer 的单位一致,因此咱们能够经过索引的形式对 buffer 指定位置进行赋值操做。
很简单吧?就是这么简单。
另外七大金刚和 Int8Array 的用法同样,可是有所不一样,咱们看下他们之间的区别。
此次咱们使用 Int16Array,仍然是刚才的 buffer,咱们建立一个新的桥梁,这座桥梁仍然通往 buffer 。
let int16Array = new Int16Array(buffer);
复制代码
你们试想一下 int16Array[0] 是什么内容? 咱们输出一下:
console.log(int16Array[0])
复制代码
咦,结果怎么是 10526?
不太理解了。好吧,咱们分析下 10526 怎么得来的。
咱们看下 buffer 中的二进制数据。
因为 Int16Array 占两个字节,因此咱们在用它读写数据的时候,一个索引所表明的数据等于 buffer 中两个字节。
咱们能够看到 int16Array[0] 里面的二进制数据是由30的二进制和41的二进制数据拼接而成:00011110(30) 00101001(41)。
咱们按照 30、41的顺序计算一下二进制对应的十进制数。
parseInt(1111000101001, 2) //输出 7721
复制代码
算出来的值是 7721,这和咱们输出的不一致呀?
这就涉及到字节顺序的概念。在咱们的我的笔记本上通常都是小端字节序。小端字节序体如今咱们这个示例中便是 4一、30的二进制顺序,咱们刚才的计算顺序有问题,那按照 4一、30 的二进制顺序计算一下
parseInt(10100100011110, 2) //输出 10526
复制代码
能够看到输出结果是 10526
,和咱们直接使用 int16Array[0]
得出的结果一致。
上面这个例子,告诉咱们在换数据结构解析 buffer 的时候,数据会变得不容易理解,咱们必定要谨慎处理。
类型化数组实例化的对象包含一些颇有用的属性和方法:
length属性返回类型化数组的数据成员个数。
返回类型化数组的字节长度。注意与length的区别。通长 byteLength = length * 每一个数据占用字节数
返回该类型化数组的数据从所处 buffer 中的哪一个字节开始。
类型化数组对应的 buffer。
复制数组,将某段内存中的数据完整地复制到另外一段内存。
let a = new Uint8Array(12);
a[0] = 31;
a[1] = 32;
let b = new Uint8Array(12);
b.set(a);
复制代码
上面这段代码的意思是将 a 这段buffer中的内容,完整地拷贝到 b 这段 buffer 中,这种方式比按索引赋值要快速地多。
固然,set 支持从某个索引开始复制数据
let a = new Uint8Array(12);
a[0] = 31;
a[1] = 32;
let b = new Uint8Array(10);
b.set(a, 2);
复制代码
上面这段代码意思是从b的第三个索引位置开始复制 a 中的数据。
subarray的意思是对一个类型化数组,取其子数组的内容,返回一个新的类型化数组。
let a = new Uint8Array(8);
a[2] = 1;
let b = a.subarray(2,3);
console.log(b.length);
console.log(b.byteLength);
复制代码
subarray 的第一个参数,表明从源数组的第几个索引开始截取,第二个参数表明截取到第几个索引。
有一点须要注意,咱们的类型数组初始化的时候,能够指定 buffer的某一段,这就意味着,咱们能够对一段 buffer 内存区域指定多个类型数,咱们称之为 混合视图
。
let buffer = new Buffer(8);
let idArray = new Int8Array(buffer, 0,2);
let nameArray = new Int8Array(buffer, 2, 4);
let ageArray = new Int8Array(buffer, 6, 2);
复制代码
咱们用一段内存区域表示一我的的 id、name、age。这种结构相似于 C 语言中的 struct 。
咱们将一段 8 个字节的内存分红三个部分:
JavaScript 还引入了另外种视图DataView
,也能达到操做 buffer 的目的,但相比之下,DataView 操做粒度更细一些,并且还可以设置字节序为大端仍是小端。
DataView 的构造函数:
DataView(ArrayBuffer对象 buffer, 从 buffer 的第几个字节开始读取, 读取的长度);
复制代码
举个例子来讲:
let buffer = new ArrayBuffer(10);
let view = new DataView(buffer);
复制代码
咱们建立好了视图 view, 那该如何读取呢?
JavaScript 提供了 8 种读取方式,功能很简单,也很容易理解,这里就不一一作示例了,你们能够本身试一下。
刚刚咱们也说了,DataView 也支持设置字节序,在上面 8 中读取方式中,第一个字节是索引,第二个字节容许咱们设置字节序,true 表明 小端字节序读取,false 表明大端字节序读取,默认为 false。
DataView 不只能支持细粒度的读取操做,也支持细粒度的写入操做:
order 的意思仍然是设置写入时的字节序, true 为小端字节序,false 为大端字节序,默认为 false。
用法也很简单,你们能够练习一下。
至此,关于 JavaScript 操做二进制的方式就介绍完了,你们之后碰到须要直接操做内存的场景时,不妨用用这两种方式。
将来的你会感谢今天努力的你。