## 循環專題
> * 循環是一種常用的流程控制。
>
>
> * `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' )) {}
> }
> ~~~