for循环
在for
循环中,可以对数组或类似数组的对象(比如arguments
和HTMLCollection
对象)进行遍历,通常for
循环模式形如:
// 非最优的循环方式
for (var i = 0; i < myarray.length; i++) {
// 访问myarray[i]…
}
这种模式的问题是,每次遍历都会访问数组的length属性,这会降低代码运行效率,特别是当myarray
不是一个数组而是一个HTMLCollection
对象的时候。
HTMLCollection
是由DOM方法返回的对象集合,比如:
- document.getElementsByName()
- document.getElementsByClassName()
- document.getElementsByTagName()
还有一些HTMLCollection
是在DOM标准诞生之前就已经在用了并且现在仍然可用,包括:
document.images
页面中所有的IMG元素
document.links
页面中所有的A元素
document.forms
页面中所有的表单
document.forms[0].elements
页面中第一个表单的所有字段
这些对象的问题在于,它们都会实时查询文档(HTML页面)中的对象。也就是说每次通过它们访问集合的length
属性时,总是都会去查询DOM,而DOM操则是很耗资源的。
更好的办法是在for
循环中缓存要遍历的数组的长度,比如下面这段代码:
for (var i = 0, max = myarray.length; i < max; i++) {
// 访问myarray[i]…
}
通过这种方法只需要获取length
一次,然后在整个循环过程中使用它。
不管在什么浏览器中,在遍历HTMLCollection
时缓存length
都可以让程序执行的更快,可以提速2倍(Safari3)到190倍(IE7)不等。更多细节可以参照Nicholas Zakas的《高性能JavaScript》,这本书也是由O’Reilly出版。
需要注意的是,当你在循环过程中需要修改这个元素集合(比如增加DOM元素)时,你可能需要更新length
。
按照“单var
模式”,你可以将var
提到循环的外部,比如:
function looper() {
var i = 0,
max,
myarray = [];
// …
for (i = 0, max = myarray.length; i < max; i++) {
// 访问myarray[i]…
}
}
当你越来越依赖“单var
模式”时,带来的好处就是提高了代码的一致性。而缺点则是在重构代码的时候不能直接复制粘贴一个循环体,比如,你正在将某个循环从一个函数复制至另外一个函数中,那么必须确保i
和max
也复制到新函数里,并且需要从旧函数中将这些没用的变量删除掉。
最后一个需要对循环做出调整的地方是将i++替换成为下面两者之一:
i = i + 1
i += 1
JSLint提示你这样做,是因为++
和--
实际上降低了代码的可读性,如果你觉得无所谓,可以将JSLint的plusplus
选项设为false
(默认为true
)。稍后,在本书所介绍的最后一个模式中用到了:i += 1
。
关于这种for
模式还有两种变化的形式,做了少量改进,原因有二:
- 减少一个变量(没有
max
) - 减量循环至0,这种方式速度更快,因为和零比较要比和非零数字或数组长度比较要高效的多
第一种变化形式是:
var i, myarray = [];
for (i = myarray.length; i--;) {
// 访问myarray[i]…
}
第二种变化形式用到了while循环:
var myarray = [],
i = myarray.length;
while (i--) {
// 访问myarray[i]…
}
这些小改进只能体现在对性能要求比较苛刻的地方,此外,JSLint不推荐使用i--
。