javascript:for循環從入門到偏門,效率優化,奇特用法


for循環是非常基礎的javascript知識,但由於JS太靈活了,所以可能出現一些讓初學者崩潰的寫法。我決定由淺入深的解釋一下for循環,算是給比我還新手的新手解惑吧,少走彎路。

一,for循環的基本寫法

代碼如下:

//例一
for
(var i=0;i<10;i++) { alert(i); }

這段代碼太簡單了,我都不好意思拿出手。代碼的執行結果是依次彈出1到10,(更正:0-9)。PS:在早期的IE如IE6中,你把10改成10000,就能讓用戶一直點確定什么也不能干了哈哈——不要說是我出的主意。

由於這是一篇基礎課程,所以我們回歸正題來詳細分析一下這段代碼吧。

for循環的結構都是類似的,任意一個for循環都是這樣的:

for(開始前;循環進行的條件;循環一次結束后做什么) {
    // 主體代碼
}

如果仔細看一下for循環,就會發現他的一個萬年不變的特點:for后的()中,永遠是有且只有兩個;(英文分號)!

上面的結構代碼已經說明,分號的作用是用來分割for循環的執行條件。這些條件都是缺一不可的,可以為空,但位置必須留着,所以必須有兩個;。

開始前:一般用來聲明一些變量,如例一的var i=0,就像准備了一個簍子,里面暫時沒有任何東西。工具的數量不限,你可以在for循環開始前聲明100個變量,除了不好看之外,也沒什么問題。

循環進行的條件:如第1個例子中的i<10,就是條件了,只有條件為真時,for循環才會進行下去,例一的條件可以看成if(i<10){//do...}。可以想象成簍子最多裝10個東西,如果多了10個,就不裝了,退出循環。

循環一次后做什么:例一中只是簡單的給簍子里面加入一個東西,其實你還可以做很多事,畢竟循環一次不容易。

特別說明:for循環“開始前”的代碼只會執行一次,不會影響整個for循環的效率,而“進行條件”與“一次結束后做什么”,你循環次數有多少,他就執行多少次,所以他們經常成為for循環性能瓶頸。

如果說for循環第1個;前是開始前做的事,那我可不可以把開始前的事情拿到for循環前面來呢?只要在開始前定義就行了嘛。答案是可以:

//例2
var i=0for(;i<10;i++) {
    alert(i);
}

但要注意,雖然for后面的括號里“開始前”已經沒有內容了,但;(分號)還在!而且必須在!

同理,既然第2個;后面的代碼是一次結束后執行的,那我也可以把要執行的放在for循環后啊。如下:

//例子3
var i=0for(;i<10;) {
    alert(i);
    i++
}

但依然,萬惡的兩個;還是必須存在。

上面也就是兩個基礎的“偏門用法”了。。。不要說我坑爹

不過你也看到了,執行循環的條件,是能單獨提出來的,必須放在兩個分號之間!前后夾擊!

二,for循環的偏門寫法

1,我們把例一的代碼變成:

var i=0;
for(;i<10;alert(i++)) ;

怎么樣?這才是坑爹啊,{}都沒有了!不過完全正確啊!

不過這種寫法太過逆天,第2個;后面的代碼最好不要多,一多起來你就不能掌握i的值了,而且因為代碼混亂可能導致人為的語法錯誤。

適用環境:

簡單的for循環操作,比如造一個元素依次是從1到1000的數字的這么一個數組,就用這招,酷就一個字。

2,深入分析

通過前面的例子我們知道,其實for循環的執行條件就是判斷一個布爾值,就像這樣:

var t = true;
if(t == true) {
    alert('啊!')
}

這個if語句沒人看不懂吧,其實還可以這樣寫:

var t = true;
if(t) {
    alert('啊!')
}

效果是一樣的,如果for循環的進行條件就是判斷布爾值,那下面這種寫法理解起來就不困難了:

var i = 10;
for(;i;i--){
    alert(i);
}

這段代碼的效果是依次彈出10到1(不是9到0)。其中for循環的進行條件簡單的要死,就是個i.但根據我們前面的解說,其實條件是這樣的:

if(i) {
    //do
}

也就是i為真的情況,就繼續執行循環。這個for循環的i什么時候為真呢,只要i不等於0,空字符串,undefined,null,false時,就都為真。

所以此for循環一直會執行,直到i=0,就結束了。但我們代碼中不會看到0 ,迷惑新手,裝B利器。

3,又一個

先看代碼,來自園友snandy:

var ary = ["jack","tom","lily","andy"];
for(var i=0,a;a=ary[i++];){
    console.log(a);
}

依然是注意看for循環的進行條件:a=ary[i++]。特別注意這里是=而不是==,要是==的話循環就沒法進行了。

這個條件判斷很扯,我也比較暈。類似於:

if(a=b) {...} //注意是=!

其實,這句代碼相當於:

a=b;
if(a){
    //do
}

就是先賦值,再判斷。此時如果b是false,那a也就是false,所以條件就成了false了。

回到上面的例子中,如果i++加出了頭,那ary[i++]就是false值了(null,undefined都算),所以條件就成了false,所以循環就斷了。

這個例子局限很大,snandy也提到了,比如你數組中就是有個0,那也可能會導致循環終結。

4,jQuery的一個寫法

function sibling( elem ) {
    var r = [],
        n = elem.parentNode.firstChild;
    for ( ; n; n = n.nextSibling ) {
        if ( n.nodeType === 1 && n !== elem ) {
            r.push( n );
        }
    }

    return r;
}

這是從jquery里提取的獲得兄弟節點的辦法,他有一個奇特的for循環。循環進行的條件是判斷n是否為真。由於n一直是一個html節點,所以一直為真。而每次循環結束后都會把n的下一個節點賦給n,而當n的下一個節點沒有時,n就成了false,終結循環。

小結:

從上面所有的例子可以看到,任他千奇百怪的for循環,都離不開兩個;。大家想看懂一個for循環的原理,直接以;為界線分割for循環即可,一目了然。

三,for循環的效率優化

1,緩存變量

這也是最常用的效率優化辦法:

var arr =[1,2,23,...,1000];
for(var i=0,l = arr.length;i<l;i++) {
    //
}

由於執行條件每次循環都要判斷,所以如果每次循環都從arr中讀取length的話,無疑很浪費而且必然增加計算次數,造成效率低下。

2,倒序法

比如一個數組有1000個元素,如果不考慮提取順序,那么可以倒序循環:

var arr =[1,2,23,...,1000];
var i = arr.length-1;
for(;i>=0;i--){
    //alert(i);
}

為什么倒序會比順序效率快?沒有科學道理啊!其實只是因為倒序可以少用一個變量(對比下上一個例子吧),除開這點,兩者沒有速度差別。

3,注意跳出

不進行不必要的操作,這是基本邏輯。如有1000個li里面,有一個li上有個特殊的className,我們要找出這個li。那么,由於已經確定只有一個這樣的li,我們找到這個li就應該馬上跳出,break,下面的循環就沒必要進行了。這樣一來,由於li有999/1000的幾率不是最后一個,我們肯定能節約不少計算。

其他情況請舉一反三。

4,使用偏門用法

上面我介紹的偏門用法不只是寫出來好看的,大部分都有節約變量節約計算的效果,能用就用,既酷又有效,何樂而不為呢?

----------------------------總結--------------------------------

我喜歡Javascript的靈活,不僅僅是因為可以用來裝酷。希望在博客園學到更多的JS知識,大牛們的文章我經常看,收益良多。下面是一些我找到的在園子里的牛人,不全,沒有列出來者不要詛咒我。

cloudgamer,司徒正美,湯姆大叔,snandy以及其他太低調的高手。想找他們的博客直接搜索吧。

PS:實在是喜歡博客園的插入代碼功能,以后涉及到大量代碼的文章都直接發博客園吧。

不要嘗試下面的代碼:

var arr =[1,2,23,1000];
for(var i=0,l = arr.length;i<l;) {
    if(arr[i]>10000) {
        i++;
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM