for循环

for循环中,可以对数组或类似数组的对象(比如argumentsHTMLCollection对象)进行遍历,通常for循环模式形如:

  1. // 非最优的循环方式
  2. for (var i = 0; i < myarray.length; i++) {
  3. // 访问myarray[i]…
  4. }

这种模式的问题是,每次遍历都会访问数组的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循环中缓存要遍历的数组的长度,比如下面这段代码:

  1. for (var i = 0, max = myarray.length; i < max; i++) {
  2. // 访问myarray[i]…
  3. }

通过这种方法只需要获取length一次,然后在整个循环过程中使用它。

不管在什么浏览器中,在遍历HTMLCollection时缓存length都可以让程序执行的更快,可以提速2倍(Safari3)到190倍(IE7)不等。更多细节可以参照Nicholas Zakas的《高性能JavaScript》,这本书也是由O’Reilly出版。

需要注意的是,当你在循环过程中需要修改这个元素集合(比如增加DOM元素)时,你可能需要更新length

按照“单var模式”,你可以将var提到循环的外部,比如:

  1. function looper() {
  2. var i = 0,
  3. max,
  4. myarray = [];
  5. // …
  6. for (i = 0, max = myarray.length; i < max; i++) {
  7. // 访问myarray[i]…
  8. }
  9. }

当你越来越依赖“单var模式”时,带来的好处就是提高了代码的一致性。而缺点则是在重构代码的时候不能直接复制粘贴一个循环体,比如,你正在将某个循环从一个函数复制至另外一个函数中,那么必须确保imax也复制到新函数里,并且需要从旧函数中将这些没用的变量删除掉。

最后一个需要对循环做出调整的地方是将i++替换成为下面两者之一:

  1. i = i + 1
  2. i += 1

JSLint提示你这样做,是因为++--实际上降低了代码的可读性,如果你觉得无所谓,可以将JSLint的plusplus选项设为false(默认为true)。稍后,在本书所介绍的最后一个模式中用到了:i += 1

关于这种for模式还有两种变化的形式,做了少量改进,原因有二:

  • 减少一个变量(没有max
  • 减量循环至0,这种方式速度更快,因为和零比较要比和非零数字或数组长度比较要高效的多

第一种变化形式是:

  1. var i, myarray = [];
  2. for (i = myarray.length; i--;) {
  3. // 访问myarray[i]…
  4. }

第二种变化形式用到了while循环:

  1. var myarray = [],
  2. i = myarray.length;
  3. while (i--) {
  4. // 访问myarray[i]…
  5. }

这些小改进只能体现在对性能要求比较苛刻的地方,此外,JSLint不推荐使用i--