|
如果你認(rèn)為引入Array.prototype.forEach及它的朋友會(huì)產(chǎn)生dodo那樣的for循環(huán),請(qǐng)?jiān)傧胂?。for循環(huán)老當(dāng)益壯。 For循環(huán)經(jīng)常被當(dāng)作一只搗蛋的小馬駒(難以馴服—譯者按)。它最適合于解決經(jīng)典的重復(fù)列舉問題: for (var i=0; i<=arr.length-1; i++) {
//do something to each member
}
但隨著更高等級(jí)命令函數(shù)的出現(xiàn),無(wú)論是在本地程序還是在架構(gòu)中,我們都可以寫以下代碼(或類似變異) arr.forEach(function(each)) {
//do something to each
});
諷刺的是,當(dāng)高等級(jí)函數(shù)逐漸取代了老舊的傳統(tǒng)模式,我們也終于可以從舊習(xí)慣中解脫出來(lái),去探索一些更有意思的for循環(huán)模式。 開胃菜——這是一個(gè)超級(jí)簡(jiǎn)潔的方法去生成并顯示出斐波那契數(shù)列中的前n位: for (
var i=2, r=[0,1];
i<15 || alert(r);
r.push(r[i-1] + r[i-2]), i++
);
//alerts "0,1,1,2,3,5,8,13,21,34,55,89,144,233,377"
基礎(chǔ)For循環(huán)的結(jié)構(gòu)包括四個(gè)部分: for (initialCode; iteratingCondition; repeatingExpression) {
repeatingCode
}
- 所有4個(gè)部分都是非必要的。 我們可以用偽代碼的方式總結(jié)整個(gè)過程——(函數(shù)調(diào)用部分單純是為了易讀性) initialCode();
while(iteratingCondition()) {
repeatingCode();
repeatingExpression();
}
探索它的模式在這一章里,for循環(huán)的用法會(huì)從我們熟悉的樣子變得有點(diǎn)古怪。這樣做的意圖是向你展示結(jié)構(gòu)的多變和語(yǔ)句的力量。我并不是為了給你提供最好的練習(xí)范本。 常見的Array模式for (var i=0; i<=arr.length-1; i++) {
var member = arr[i];
doSomething(member);
}
儲(chǔ)存array長(zhǎng)度以提高效率for (var i=0, l=arr.length; i<=l-1; i++) {
var member = arr[i];
doSomething(member);
}
合并iteratingCondition和repeatingExpressionfor (var i=arr.length; i--;) {
var member = arr[i];
doSomething(member);
}
這樣做的原理是當(dāng)i為0, 判定條件變?yōu)閒alse,我們會(huì)退出循環(huán)。當(dāng)然,只有我們可以使用逆向循環(huán)的時(shí)候,以上代碼才可用。 在iteratingCondition中給變量賦值我們可以把repeatingCode中的變量賦值移到theriteratingCondition中去。當(dāng)each無(wú)定義時(shí),循環(huán)就會(huì)被終止。 這樣我們就縮短了代碼的長(zhǎng)度并且不需要檢查數(shù)列長(zhǎng)度。語(yǔ)法變得更直接,在我看來(lái),也更優(yōu)雅。只有當(dāng)數(shù)列是連續(xù)的,并且不會(huì)有出現(xiàn)“falsey”值(null,0,空或false)的時(shí)候,這個(gè)方法才可用。 for (var i=0, each; each = arr[i]; i++) {
doSomething(each);
}
稀疏數(shù)列測(cè)試我們可以倒轉(zhuǎn)以上的模式來(lái)主動(dòng)地檢查一個(gè)稀疏的數(shù)列或列表。這里我們高效地檢測(cè)未定義參數(shù): var func = function(a,b,c) {
for (var i=0; arguments[i] !== undefined; i++);
var allArguments = (i >= arguments.callee.length);
//...
}
無(wú)repeatingCode部分repeatingCode和repeatingExpression有著相同的用途。所以如果重復(fù)代碼部分可以簡(jiǎn)單地總結(jié)為一句代碼,你可以刪掉整個(gè)repeatCode部分。 function sum(arr) {
for (var i=arr.length, r=0; i--; r += arr[i]);
return r;
}
sum([3,5,0,-2,7,8]); //21
在iteratingCondition中藏一個(gè)finally子句我們可以使用 邏輯布爾值||運(yùn)算符 來(lái)定義一個(gè)final語(yǔ)句。這個(gè)小函數(shù)會(huì)把一個(gè)數(shù)組中的值相加,并在完成后打印出這個(gè)值。 function shoutOutSum(arr, x) {
for (var i=arr.length, r=0; i-- || alert(r); r += arr[i]);
}
shoutOutSum([3,5,0,-2,7,8]); //alerts "21"
當(dāng)然如果你的最終子句不返回falsey值的話,你就有麻煩了。你會(huì)陷入死循環(huán)。為保證這不會(huì)發(fā)生,你把final變量和false &&上。這會(huì)顯得有點(diǎn)笨拙: function sumAndMultiply(arr, x) {
for (var i=arr.length, r=0; i-- || ((r = r*x) && false); r += arr[i]);
return r;
}
sumAndMultiply([3,5,0,-2,7,8], 5); //105
更新:Brendan Eich建議用void來(lái)替代: function sumAndMultiply(arr, x) {
for (var i=arr.length, r=0; i-- || void (r = r*x); r += arr[i]);
return r;
}
在initialCode部分無(wú)變量定義initialCode不需要變量定義。為了不因variable hoisting產(chǎn)生混淆,很多程序員在函數(shù)的開頭定義所有變量,有些JavaScript專家(包括Douglas Crockford)甚至不會(huì)再for循環(huán)中定義變量。 function myFunction(arr) {
var i;
//...
for (i=0; i < arr.length; i++) {
//...
}
//...
}
像我之前所說(shuō),你幾乎一定會(huì)想用initialCode來(lái)給一些變量賦值,但這不是必須的。以下代碼是一個(gè)挺爛的for循環(huán),但我只是想用它來(lái)證明這一點(diǎn)。 var i = 0;
for (
console.log('start:',+new Date);
i<1000 || console.log('finish:',+new Date);
i++
);
總結(jié)本文只是列舉了幾種傳統(tǒng)for循環(huán)語(yǔ)法的變形。毫無(wú)疑問,你還會(huì)用其它一些技術(shù),我也很想聽你說(shuō)說(shuō)看。我并不是建議你從明天開始就急匆匆地使用所有這些模式;即使你完全不用也沒有關(guān)系!然而,舊瓶裝新酒是加強(qiáng)對(duì)一個(gè)語(yǔ)言深度了解的好方法,最終也會(huì)保證這個(gè)語(yǔ)言自身的發(fā)展和成功。 繼續(xù)閱讀ECMA-262, 5th edition |
|
|
來(lái)自: 燮羽 > 《Javascript》