JS中类数组的遍历以及性能探究

类数组

JS 中我们都知道有一种数据类型叫数组,平常也用得很多,这里看到数组的介绍。但什么是类数组呢?
JS 中存在一些对象,它们都带有 length 属性,却又不具备数组的那些方法,这些对象我们它叫做类数组。那么类数组有哪些呢?

  • jQuery 对象
1
2
var aList = $(".tagcloud").find("a");
console.log(aList.length); // >= 0

如上通过 jQuery 获取的元素集合就是一个典型的类数组。

  • NodeList
1
2
var h2s = document.querySelectorAll("h2");
console.log(h2s.constructor); // ƒ NodeList() { [native code] }

NodeList是节点集合,既包括了元素节点也包含了文本节点,比如node.childNodes也是一个 NodeList

  • HTMLCollection
1
2
var h4s = document.getElementsByTagName("h4");
console.log(h4s instanceof HTMLCollection); // true

HTMLCollection 是元素集合,更多详细信息可以看这里

类数组虽然没有数组的那些方法,但是可以通过 call 方法将其转成数组:

1
2
3
4
var h2s = document.querySelectorAll("h2");
console.log(h2s.constructor); // ƒ NodeList() { [native code] }
var temp = Array.prototype.slice.call(h2s, null);
console.log(temp instanceof Array); // true

遍历类数组

数组或者类数组的遍历方式很多,下面我们一一来介绍。

  • 普通 for 循环
1
for (var i = 0; i < arr.length; i++) {}

最普通也是最经典的 for 循环,大多数开发都喜欢用的方式。

  • 优化版 for 循环
1
for (var i = 0, l = arr.length; i < l; i++) {}

优化后的 for 循环,把数组长度的取值放到了定义变量的时候,避免了每次判断都要去取数组长度,从而节约了一定时间。

  • 弱化版 for 循环
1
for (var i = 0; arr[i] != null; i++) {}

这种循环方式和上面两种都差不了多少,只不过是没有使用 length 判断,而使用变量本身判断。

  • forEach 循环
1
arr.forEach(function (item) {});

JS 数组自带的遍历方法。

  • 新版 forEach
1
Array.prototype.forEach.call(arr, function (ele) {});

借用了原型的 call 方法来遍历类数组。

  • for in循环
1
2
for (var temp in arr) {
}
  • for of循环
1
2
for (let v of arr) {
}

ES6中新提出的一种循环方式。

  • map循环
1
arr.map(function (e, i) {});
  • while循环
1
2
var flag = 0;
while (flag++ < arr.length) {}

性能测试

这里测试里主要是通过控制外循环次数、数组的长度和循环体的复杂度来达到模拟性能测试的目的。

array_like_01.png

1
2
3
4
5
6
7
8
// 核心代码
var t4 = new Date().getTime();
for ( var j = 0; j < count; j++ ) { // 外循环
arr.forEach(function( cha ) { // 内循环
contextFn( cha ); // 循环体
});
}
console.log( new Date().getTime() - t4) );

内循环里是分别替换到不同的遍历方式,通过设置循环次数和数组长度来测试了一些数据。

array_like_02.png

从上面的测试结果大致可以看出速度最快的遍历方式应该是优化后的 for 循环了。

background_01.png