循环是一种常用的流程控制。
JAVASCRIPT
提供了三种循环。
for(;;)
。
- 推荐使用for循环,如果循环变量递增或递减,不要单独对循环变量赋值,而应该使用嵌套的
++
或–-
运算符。- 代码的可读性对于for循环的优化。
- 用
-=1
。从大到小的方式循环(这样缺点是降低代码的可读性)。
/**效率低**/ var divs = document.getElementsByTagName("div"); for(var i = 0; i < divs.length; i++){ ... } /**效率高,适用于获取DOM集合,如果纯数组则两种情况区别不到**/ var divs = document.getElementsByTagName("div"); for(var i = 0, len = divs.length; i < len; i++){ ... } /**在`IE6.0`下,`for(;;)`循环在执行中,第一种情况会每次都计算一下长度,而第二种情况却是在开始的时候计算长度,并把其保存到一个变量中,所以其执行效率要高点,所以在我们使用`for(;;)`循环的时候,特别是需要计算长度的情况,我们应该开始将其保存到一个变量中。**/while()
。
for(;;)
、while()
循环的性能基本持平。for(in)
。
- 在这三种循环中
for(in)
内部实现是构造一个所有元素的列表,包括array
继承的属性,然后再开始循环,并且需要查询hasOwnProperty。所以for(in)
相对for(;;)
循环性能要慢。选择正确的方法
避免不必要的属性查找。
- 访问
变量
或数组
是O(1)
操作。- 访问
对象
上的属性
是一个O(n)
操作。对象上的任何属性查找都要比访问变量或数组花费更长时间,因为必须在原型链中对拥有该名称的属性进行一次搜索,即属性查找越多,执行时间越长。所以针对需要多次用到对象属性,应将其存储在局部变量。
优化循环。
- 减值迭代。
- 大多数循环使用一个从0开始,增加到某个特定值的迭代器。在很多情况下,从最大值开始,在循环中不断减值的迭代器更加有效。
- 简化终止条件。
- 由于每次循环过程都会计算终止条件,故必须保证它尽可能快,即避免属性查找或其它O(n)的操作。
- 简化循环体。
- 循环体是执行最多的,故要确保其被最大限度地优化。确保没有某些可以被很容易移出循环的密集计算。
- 使用后测试循环。
- 最常用的for和while循环都是前测试循环,而如do-while循环可以避免最初终止条件的计算,因些计算更快。 > for(var i = 0; i 优化1:简化终止条件 > for(var i = 0, len = values.length; i 优化2:使用后测试循环(注意:使用后测试循环需要确保要处理的值至少有一个) > var i values.length - 1; if(i > -1) { do { process(values[i]); }while(--i >= 0); }
- 展开循环。
- 当循环的次数确定时,消除循环并使用多次函数调用往往更快。
- 当循环的次数不确定时,可以使用Duff装置来优化。
- Duff装置的基本概念是通过计算迭代的次数是否为8的倍数将一个循环展开为一系列语句。 > // Jeff Greenberg for JS implementation of Duff's Device // 假设:values.length > 0 function process(v) { alert(v); } var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]; var iterations = Math.ceil(values.length / 8); var startAt = values.length % 8; var i = 0; do { switch(startAt) { case 0 : process(values[i++]); case 7 : process(values[i++]); case 6 : process(values[i++]); case 5 : process(values[i++]); case 4 : process(values[i++]); case 3 : process(values[i++]); case 2 : process(values[i++]); case 1 : process(values[i++]); } startAt = 0; }while(--iterations > 0); > 如上展开循环可以提升大数据集的处理速度。接下来给出更快的Duff装置技术,将do-while循环分成2个单独的循环。(注:这种方法几乎比原始的Duff装置实现快上40%。) > // Speed Up Your Site(New Riders, 2003) function process(v) { alert(v); }
var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]; var iterations = Math.floor(values.length / 8); var leftover = values.length % 8; var i = 0; if(leftover > 0) { do { process(values[i++]); }while(--leftover > 0); }
do { process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); }while(--iterations > 0); > 针对大数据集使用展开循环可以节省很多时间,但对于小数据集,额外的开销则可能得不偿失。避免在循环中使用
try-catch
。
try-catch-finally
语句在catch语句被执行的过程中会动态构造变量插入到当前域中,对性能有一定影响。如果需要异常处理机制,可以将其放在循环外层使用。
循环中使用try-catch
for ( var i = 0; i < 200; i++) { try {} catch (e) {} }循环外使用try-catch
try { for ( var i = 0; i < 200; i++) {} } catch (e) {}避免遍历大量元素:
避免对全局
DOM
元素进行遍历,如果parent
已知可以指定parent
在特定范围查询。var elements = document.getElementsByTagName( '*' ); for (i = 0; i < elements.length; i++) { if (elements[i].hasAttribute( 'selected' )) {} }如果已知元素存在于一个较小的范围内,
var elements = document.getElementById( 'canvas' ).getElementsByTagName ( '*' ); for (i = 0; i < elements.length; i++) { if (elements[i].hasAttribute( 'selected' )) {} }