视图类型
ArrayBuffer, TypedArray, DataView
在剖析 V8 引擎一节中我们讨论过,目前 JavaScript 使用的数组实际上是伪数组。这种伪数组给我们的操作带来了极大的方便性,但这种实现方式也带来了另一个问题,及无法达到数组快速索引的极致。上百万的数据量的情况下,每次新添加一条数据都需要动态分配内存空间,数据索引时都要遍历链表索引造成的性能浪费会变得异常的明显。
在 ES6 中,JS 新提供了一种获得真正数组的方式:ArrayBuffer,TypedArray 和 DataView。ArrayBuffer 代表分配的一段定长的连续内存块。但是我们无法直接对该内存块进行操作,只能通过 TypedArray 和 DataView 来对其操作。
const ab = new ArrayBuffer(32);
const iA = new Int8Array(ab);
iA[0] = 97; //把二进制的数据的首位改为97,97为小写字母a的 ascii 码;
const blob = new Blob([iA], { type: "application/octet-binary" }); //把二进制的码转化为blob类型
const url = URL.createObjectURL(blob);
window.open(url);
如果希望将 Blob 转化为 ArrayBuffer,则需要使用到 FileReader:
// ArrayBuffer -> Blob
const uint8Array = new Uint8Array([1, 2, 3]);
const arrayBuffer = uint8Array.buffer;
const blob = new Blob([arrayBuffer]);
// Blob -> ArrayBuffer
const uint8ArrayNew = null;
const arrayBufferNew = null;
const fileReader = new FileReader();
fileReader.onload = function (event) {
arrayBufferNew = event.target.result;
uint8ArrayNew = new Uint8Array(arrayBufferNew);
// warn if read values are not the same as the original values
// arrayEqual from: http://stackoverflow.com/questions/3115982/how-to-check-javascript-array-equals
function arrayEqual(a, b) {
return !(a < b || b < a);
}
if (arrayBufferNew.byteLength !== arrayBuffer.byteLength)
// should be 3
console.warn("ArrayBuffer byteLength does not match");
if (arrayEqual(uint8ArrayNew, uint8Array) !== true)
// should be [1,2,3]
console.warn("Uint8Array does not match");
};
fileReader.readAsArrayBuffer(blob);
fileReader.result; // also accessible this way once the blob has been read
TypedArray
TypeArray 是一个统称,包含 Int8Array, Int16Array, Int32Array, Float32Array 等等。以 Int8Array 为例,这个对象可拆分为三个部分:Int、8、Array:首先这是一个数组,这个数据里存储的是有符号的整形数据,每条数据占 8 个比特位,及该数据里的每个元素可表示的最大数值是 2^7 = 128, 最高位为符号位。
// TypedArray
const typedArray = new Int8Array(10);
typedArray[0] = 8;
typedArray[1] = 127;
typedArray[2] = 128;
typedArray[3] = 256;
console.log("typedArray", " -- ", typedArray);
//Int8Array(10) [8, 127, -128, 0, 0, 0, 0, 0, 0, 0]
其他类型也都以此类推,可以存储的数据越长,所占的内存空间也就越大。这也要求在使用 TypedArray 时,对你的数据非常了解,在满足条件的情况下尽量使用占较少内存的类型。
DataView
DataView 相对 TypedArray 来说更加的灵活。每一个 TypedArray 数组的元素都是定长的数据类型,如 Int8Array 只能存储 Int8 类型;但是 DataView 却可以在传递一个 ArrayBuffer 后,动态分配每一个元素的长度,即存不同长度及类型的数据。
// DataView
const arrayBuffer = new ArrayBuffer(8 * 10);
const dataView = new DataView(arrayBuffer);
dataView.setInt8(0, 2);
dataView.setFloat32(8, 65535);
// 从偏移位置开始获取不同数据
dataView.getInt8(0);
// 2
dataView.getFloat32(8);
// 65535
DataView 最大的性能问题在于将 JS 转成 C++ 过程的性能浪费。而谷歌将该部分使用 CSA(CodeStubAssembler)语言重写后,可以直接操作 TurboFan(V8 引擎)来避免转换时带来的性能损耗。
性能对比
// 普通数组
function arrayFunc() {
const length = 2e6;
const array = [];
const index = 0;
while (length--) {
array[index] = 10;
index++;
}
}
// dataView
function dataViewFunc() {
const length = 2e6;
const arrayBuffer = new ArrayBuffer(length);
const dataView = new DataView(arrayBuffer);
const index = 0;
while (length--) {
dataView.setInt8(index, 10);
index++;
}
}
// typedArray
function typedArrayFunc() {
const length = 2e6;
const typedArray = new Int8Array(length);
const index = 0;
while (length--) {
typedArray[index++] = 10;
}
}