视图类型

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;
  }
}
上一页
下一页