2

Javascript的For循环

 2 years ago
source link: http://fantaghiro.github.io/study/2016/04/28/JS-For-Loop-Study-Notes.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Javascript的For循环

2016-04-28

Eloquent Javascript第二章的第一个练习题十分简单,题目要求利用console.log打出下面一个三角形状:

#
##
###
####
#####
######
#######

第一反应是for循环,用for(var i = 1; i < 8; i++)。但是看过答案之后,发现自己的方法还是比较笨的。在作者提供的答案中,是这样写的:

for(var str = "#"; str.length < 8; str += "#"){
    console.log(str);
}

我个人感觉十分优雅,同时也感觉到自己以前对for循环的理解有些太机械化了。在w3schools上,对于for循环是这样明确说明的:

for (语句1; 语句2; 语句3)
{
    被执行的代码块
}
//语句1:在循环(代码块)开始前执行
//语句2:定义运行循环(代码块)的条件
//语句3:在循环(代码块)已被执行之后执行

针对for循环,这里 有一篇非常好的文章。在此记录下其中的几个例子。

通常情况下,for循环是这样用的:

for(var i = 0; i < arr.length; i++){
    //do something to each member
}

但事实上,对于数组的遍历,目前使用高阶函数的形式更为普遍,如下:

arr.forEach(function(item){
    //do something to item
})

虽然如此,for循环作为js中的老鸟,还是有些奇技淫巧的,例如文章作者提供了下面这个生成菲波那切数列的代码,用的就是for循环:

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循环,包含以下四个部分:

for (初始代码; 循环条件; 重复的表达式){
    重复的代码
}
  • 以上所有四个部分都是可选的(不必须)
  • 初始代码不非得是变量赋值——任何合法的表达式都可以
  • 循环条件和重复表达式不能包含变量声明
  • 如果重复代码值包含一句或更好,那么大括号也不是必须的
  • 重复的表达式是在重复的代码执行完成之后才进行运算的

将上面的for循环用while循环来模拟,就是下面这个样子:

初始代码();
while(循环条件()){
    重复的代码();
    重复的表达式();
}

探索使用模式

传统的数组遍历

for(var i = 0; i < arr.length; i++){
    var member = arr[i];
    doSomething(member);
}

存储数组长度以提高效率

for(var i = 0, l = arr.length; i < l; i++){
    var member = arr[i];
    doSomething(member);
}

针对这一应用,有人向作者提出异议,认为l会变成全局变量。作者后续举例说明,此处的l仍然是局部变量:

var l;
var test = function(arr) {
    for (var i=0, l=arr.length; i<l; i++) {
        var member = arr[i];
    }
    alert(l);
};
test([0,1,2]); //运行到test里面的alert,弹出3
alert(l); //运行到本句,弹出undefined

将循环条件和重复表达式合并

for (var i = arr.length; i--){
    var member = arr[i];
    doSomething(member);
}

当i减到0的时候,循环条件即为假,于是循环就停止了。当然,这只适用于你可以倒着遍历的时候。

在循环条件那里为member赋值

一般member的赋值都是在大括号里面,其实可以将其移到循环条件处。一旦each是undefined的时候,循环就停止了。

for(var i = 0, each; each = arr[i]; i++){
    doSomething(each);
}
for(var each, i = -1; item = arr[++i];){
    doSomething(each);
}

这种写法只适用于密集数组,而且数组里面不能有null、0、“”、或false这样的值。

稀疏数组的检测

我们可以将上述模式反过来,检查稀疏数组。下面就是有效检测undefined参数:

var func = function(a, b, c){
    for(var i = 0; arguments[i] !== undefined; i++);
    var allArguments = (i >= arguments.callee.length);
    //...
}

省略大括号

大括号里面重复的代码和小括号里面重复的表达式,其实目的都一样。因此,如果重复代码可以很轻易地放到重复表达式里面,那么整个大括号也就不需要了:

function sum(arr){
    for(var i = arr.length, r = 0; i--; r += arr[i]);
    return r;
}
sum([3, 5, 0, -2, 7, 8]); //21

在循环条件那里藏着一个最终finally执行的语句

我们可以使用布尔运算符 || 来定义循环结束之后,finally运行的一个语句。例如,上面的sum函数,可以设置在运算结束后,alert结果:

function sum(arr){
    for(var i = arr.length, r = 0; i-- || alert(r); r += arr[i]);
    return r;
}

但是,如果你循环结束后finally执行的语句返回的不是false的值,那么就陷入无限循环了。因此,要避免这样的问题,可以在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

也有人推荐使用void运算符:

function sumAndMultiply(arr, x) {
        for (var i=arr.length, r=0; i-- || void (r = r*x); r += arr[i]);
    return r;
}

在初始代码处不进行变量声明

function myFunction(arr){
    var i;
    //...
    for(i = 0; i < arr.length; i++){
        //...
    }
    //...
}

下面的这种用法不见得好,但是你能看到,初始代码里确实不非得声明变量:

var i = 0;
for (
    console.log('start:', +new Date);
    i < 1000 || console.log('finish:', +new Date);
    i++
)

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK