## 循环专题
> * 循环是一种常用的流程控制。
>
>
> * `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' )) {}
> }
> ~~~