类型化数组与常规数组的区别
二者最重要的区别就是类型化数组并不是常规数组,类型化数组并不是从 Array 对象派生的,使用 Array.isArray() 去检测会返回 false,例如:
let ints = new Int16Array([25, 50]);
console.log(ints instanceof Array); // false
console.log(Array.isArray(ints)); // false
由于 ints 变量是一个类型化数组,因此它并不是 Array 对象的实例,于是就不会被识别为数组。这一点区别很重要,因为虽然类型化数组与常规数组非常相似,但前者仍然有一些不同的行为。
行为差异
常规数组可以被伸展或是收缩,然而类型化数组却会始终保持自身大小不变。你可以对常规数组一个不存在的索引位置进行赋值,但在类型化数组上这么做则会被忽略。这里有个例子:
let ints = new Int16Array([25, 50]);
console.log(ints.length); // 2
console.log(ints[0]); // 25
console.log(ints[1]); // 50
ints[2] = 5;
console.log(ints.length); // 2
console.log(ints[2]); // undefined
在本例中,尽管对索引值 2 的位置进行了赋值为 5 的操作,但 ints 数组却完全没有被伸展,数组的长度属性保持不变,所赋的值也被丢弃了。
类型化数组也会对数据类型进行检查以保证只使用有效的值,当无效的值被传入时,将会被替换为 0,例如:
let ints = new Int16Array(["hi"]);
console.log(ints.length); // 1
console.log(ints[0]); // 0
这段代码试图用字符串值 "hi" 创建一个 Int16Array,而字符串对于类型化数组来说当然是无效的值,因此该字符串被替换为 0 并插入数组。此数组的长度仅仅是 1,而 ints[0] 只包含了 0 这个值。
所有在类型化数组上修改项目值的方法都会受到相同的限制,例如当 map() 方法使用的映射函数返回一个无效值的时候,类型化数组会使用 0 来代替返回值:
let ints = new Int16Array([25, 50]),
mapped = ints.map(v => "hi");
console.log(mapped.length); // 2
console.log(mapped[0]); // 0
console.log(mapped[1]); // 0
console.log(mapped instanceof Int16Array); // true
console.log(mapped instanceof Array); // false
由于字符串值 "hi" 并不是一个 16 位整数,它在结果数组中就被替换成为 0。多亏这种纠错行为,类型化数组的内容永远不会是无效值,因此相关方法就无须再担心传入无效值会导致错误。
遗漏的方法
尽管类型化数组拥有常规数组的很多同名方法,但仍然缺少了几个数组方法,包括下列这些
-
concat()
-
pop()
-
push()
-
shift()
-
splice()
-
unshift()
除了 concat() 方法之外,该列表中的其余方法都会改变数组的大小,而由于类型化数组的大小不可变,因此这些方法都不能作用于类型化数组。concat() 方法不可用的原因则是:连接两个类型化数组的结果是不确定的(特别是当它们处理的数据类型不同时),这种不确定情况原本就不应当使用类型化数组。
附加的方法
最后,类型化数组还有两个常规数组所不具备的方法:set() 方法与 subarray() 方法。这两个方法作用相反:set() 方法从另一个数组中复制元素到当前的类型化数组,而 subarray() 方法则是将当前类型化数组的一部分提取为新的类型化数组。
set() 方法接受一个数组参数(无论是类型化的还是常规的)、以及一个可选的偏移量参数,后者指示了从什么位置开始插入数据(默认值为 0)。数组参数中的数据会被复制到目标类型化数组中,并会确保数据值有效。这里有个例子:
let ints = new Int16Array(4);
ints.set([25, 50]);
ints.set([75, 100], 2);
console.log(ints.toString()); // 25,50,75,100
这段代码创建了一个包含四个元素的 Int16Array;第一次调用 set() 复制了两个值到数组起始的两个位置;而第二次调用 set() 则使用了一个值为 2 的偏移量参数,指明应当从数组的第三个位置(索引 2)开始放置所复制的数据。
subarray() 方法接受一个可选的开始位置索引参数、以及一个可选的结束位置索引参数(像 slice() 方法一样, 结束位置的元素不会被包含在结果中),并会返回一个新的类型化数组。你可以同时省略这两个参数,从而创建原类型化数组的一个复制品。例如:
let ints = new Int16Array([25, 50, 75, 100]),
subints1 = ints.subarray(),
subints2 = ints.subarray(2),
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
本例中利用 ints 数组创建了三个类型化数组。subints1 数组是 ints 的一个复制品,包含了原数组的所有信息;而 subints2 则从原数组的索引 2 位置开始复制,因此包含了原数组的最末两个元素(即 75 与 100);最后的 subints3 数组值包含了原数组的中间两个元素,因为调用 subarray() 时同时使用了起始位置与结束位置参数。