如果你认为引入Array.prototype.forEach及它的朋友会产生dodo那样的for循环,请再想想。for循环老当益壮。 For循环经常被当作一只捣蛋的小马驹(难以驯服—译者按)。它最适合于解决经典的重复列举问题: for (var i=0; i<=arr.length-1; i++) { //do something to each member } 但随着更高等级命令函数的出现,无论是在本地程序还是在架构中,我们都可以写以下代码(或类似变异) arr.forEach(function(each)) { //do something to each }); 讽刺的是,当高等级函数逐渐取代了老旧的传统模式,我们也终于可以从旧习惯中解脱出来,去探索一些更有意思的for循环模式。 开胃菜——这是一个超级简洁的方法去生成并显示出斐波那契数列中的前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"
基础For循环的结构包括四个部分: for (initialCode; iteratingCondition; repeatingExpression) { repeatingCode } - 所有4个部分都是非必要的。 我们可以用伪代码的方式总结整个过程——(函数调用部分单纯是为了易读性) initialCode(); while(iteratingCondition()) { repeatingCode(); repeatingExpression(); } 探索它的模式在这一章里,for循环的用法会从我们熟悉的样子变得有点古怪。这样做的意图是向你展示结构的多变和语句的力量。我并不是为了给你提供最好的练习范本。 常见的Array模式for (var i=0; i<=arr.length-1; i++) { var member = arr[i]; doSomething(member); } 储存array长度以提高效率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); } 这样做的原理是当i为0, 判定条件变为false,我们会退出循环。当然,只有我们可以使用逆向循环的时候,以上代码才可用。 在iteratingCondition中给变量赋值我们可以把repeatingCode中的变量赋值移到theriteratingCondition中去。当each无定义时,循环就会被终止。 这样我们就缩短了代码的长度并且不需要检查数列长度。语法变得更直接,在我看来,也更优雅。只有当数列是连续的,并且不会有出现“falsey”值(null,0,空或false)的时候,这个方法才可用。 for (var i=0, each; each = arr[i]; i++) { doSomething(each); } 稀疏数列测试我们可以倒转以上的模式来主动地检查一个稀疏的数列或列表。这里我们高效地检测未定义参数: var func = function(a,b,c) { for (var i=0; arguments[i] !== undefined; i++); var allArguments = (i >= arguments.callee.length); //... } 无repeatingCode部分repeatingCode和repeatingExpression有着相同的用途。所以如果重复代码部分可以简单地总结为一句代码,你可以删掉整个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中藏一个finally子句我们可以使用 逻辑布尔值||运算符 来定义一个final语句。这个小函数会把一个数组中的值相加,并在完成后打印出这个值。 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" 当然如果你的最终子句不返回falsey值的话,你就有麻烦了。你会陷入死循环。为保证这不会发生,你把final变量和false &&上。这会显得有点笨拙: 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来替代: function sumAndMultiply(arr, x) { for (var i=arr.length, r=0; i-- || void (r = r*x); r += arr[i]); return r; } 在initialCode部分无变量定义initialCode不需要变量定义。为了不因variable hoisting产生混淆,很多程序员在函数的开头定义所有变量,有些JavaScript专家(包括Douglas Crockford)甚至不会再for循环中定义变量。 function myFunction(arr) { var i; //... for (i=0; i < arr.length; i++) { //... } //... } 像我之前所说,你几乎一定会想用initialCode来给一些变量赋值,但这不是必须的。以下代码是一个挺烂的for循环,但我只是想用它来证明这一点。 var i = 0; for ( console.log('start:',+new Date); i<1000 || console.log('finish:',+new Date); i++ ); 总结本文只是列举了几种传统for循环语法的变形。毫无疑问,你还会用其它一些技术,我也很想听你说说看。我并不是建议你从明天开始就急匆匆地使用所有这些模式;即使你完全不用也没有关系!然而,旧瓶装新酒是加强对一个语言深度了解的好方法,最终也会保证这个语言自身的发展和成功。 继续阅读ECMA-262, 5th edition |
|
来自: 燮羽 > 《Javascript》