6

本文章记录本人在深入学习js循环中看书理解到的一些东西,加深记忆和并且整理记录下来,方便之后的复习。

选择正确的循环体

在大部分编程语言中,代码执行的时间多数消耗在循环的执行上。

js定义了4种类型的循环:for, while, do while, for in

for循环是最常用的循环结构,它由四个部分组成:

  1. 初始化体
  2. 前测条件
  3. 后执行条件
  4. 循环体。

当遇到一个for循环的时候,初始化体首先执行,然后进入前测条件。如果前测条件的计算为true,就会执行循环体,然后运行后执行条件。

Javascriptfor (var i = 0; i < 10; i++) {
    // 循环体
};

while循环是一个简单的前测循环,由一个前测条件和一个循环体构成。在执行循环体之前,先对前测条件进行计算。如果为true就执行循环体。否则就会跳过循环体。

Javascriptvar i = 1;
while (i < 10) {
    // 循环体
    i ++;
};

do while循环是js中唯一的一种后测试的循环,它包括两个部分:循环体和后侧条件。记住:在一个do while循环中,循环体至少会运行一次,后测条件决定循环体是否再次运行。

Javascriptvar i = 1;
do {
    // 循环体
} while (i++ < 10)

for in循环有一个非常特殊的用途:可以枚举任何对象的命名函数属性。

Javascriptfor (var attr in object) {
    // 循环体
};

每一次循环,属性都会被对象的属性的名字(一个字符串)填充,直到所有的对象属性遍历完才会返回。返回的属性包括对象的使实例属性和对象从原型链基础来的属性。

想要提高循环性能,第一步选用哪种循环。在这四个循环里面,for in循环执行的速度都比其他三个循环要慢。

比较 for 和 while

大部分情况下我们都不会采用for in循环,但是其他的循环类型性能都相当,难以确定哪种循环执行速度更快。选择循环类型应该基于需求而不是性能。

可以通过设计forwhile循环来完成特定动作的重复性操作。从3个目标来分析如何正确的选用forwhile循环。

语义角度比较

forwhile循环可以按照下面的模式来进行互相的转换。

Javascriptfor (initialization; test; increment) // 声明并且初始化循环变量,循环条件,递增循环变量
    statements // 可执行的循环语句

相当于:

Javascriptinitialization  // 声明并且初始化循环变量
while (test) {  // 循环条件
    statements  // 可执行的循环语句
    increment   // 递增循环变量
}

for循环是以循环变量的变化来控制循环进程的,即for循环流程是预先计划好的,虽然中途可能会因存在异常情况或特别情况而退出循环,但是循环的规律是有章可循的。这样的话就方便预知循环的次数和每次循环的状态信息等等。

while循环根据特定条件来决定循环操作,由于条件是动态的,无法预知条件几时是true或者false,因此该循环操作就具有很大的不确定性,每一次循环时都不知道下一次循环状态如何,只能通过条件的变化来确定。

所以for循环通常用于有规律的重复操作中。while循环通常用于特定条件的重复操作,以及依据特定事件控制的循环等操作。

思维模式角度比较

for循环和while循环在思维模式上也存在差异。

for循环中,将循环的三个要素(起始值、终止值和步长)定义为3个基本表达式作为结构语法的一部分固定在for语句里面,使用小括号;进行语法分隔,这样有利于js的进行快速预编译。

在阅读到for循环结构的第一行代码的时候,就能够获取到整个循环结构的控制方式,然后再根据上面的表达式来决定是否执行循环体内的语句。

Javascriptfor (var i = 0; i < 10; i++) {
    alert(i);
};

可以按照下面的逻辑思维进行总结:

  • 执行循环条件:1 < i < 10、步长为i++
  • 执行循环语句:alert(i);

这种把循环操作的环境条件和循环操作语句分离开的设计能够提高程序的执行效率,同时能够避免因为把循环条件与循环语句混合在一起而造成的错误。

for循环的特异性导致在执行复杂的条件时效率会大大的降低。相对而言,while循环就比较适合运http://naotu.baidu.com/viewshare.html?shareId=auuohz8fwm8k的条件,它将复杂的循环控制放在循环体内执行,而while语句自身仅用于测试循环条件,这样就避免了结构的分隔和逻辑的跳跃。

Javascriptvar a = true, b = 1;
while (a) {  // 在循环体内间接计算迭代
    if (9 < b) {
        a = false;
    }
    alert(b);
    b++;
}

从达到目标的角度比较

有一些循环的循环次数在循环之前就可以先预测,如计算1到100的数字和。而有一些循环具有不可预测性们无法事前确定循环的次数,甚至无法预知循环操作的趋向,这些构成了在设计循环结构时候必须考虑的达成目标需要解决的问题,即使是相同的操作,如果达到的目标的角度不同,可能重复了操作的设计也就不同。

使用递归

许多复杂的算法都是通过比较容易实现的递归实现。如阶乘函数。

Javascriptfunction factorial(n) {
    if (0 === n) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
};

递归函数的问题:错误定义或者缺少终结条件会导致函数运行时间过长,使浏览器出现假死现象。递归函数还会受到浏览器调用栈的大小的限制。

在ES6中js拥有了尾递归优化,详情请看迎接ECMAScript 6, 使用尾递归

使用迭代

可以通过递归函数实现的算法都可以通过迭代来实现。迭代算法通常包括几个不同的循环,分别对应算法过程中的不同方面。虽然迭代也会导致性能问题,但是使用优化的循环就可以代替长时间运行的递归函数,可以提高新能,因为运行一个循环比反复调用一个函数的开销要小。

使用迭代来实现合并排序算法:

Javascriptfunction merge(left, right) {
    var result = [];
    while (left.length > 0 && right.length > 0) {
        if (left[0] < right[0]) {
            result.push(left.shift());
        } else {
            result.push(right.shift());
        }
    }
    return result.concat(left).concat(right);
};
function mergeSort(items) {
    if (items.length === 1) {
        return items;
    }
    var work = [];
    for (var i = 0, len = items.length; i < len; i++) {
        work.push(items[i]);
    };
    work.push([]);
    for (var lim = len; lim > 1; lim = (lim + 1) / 2) {
        for (var j = 0, k = 0; k < lim; j++, k += 2) {
            work[j] = merge(work[k], work[k + 1]);
        }
        work[j] = [];
    };
    return work[0];
};

优化循环结构

循环结构是最浪费资源的一种流程。循环结构中的一点小的损耗都会被放大,从而影响程序运行的效率。

优化结构

循环结构常常是与分支结构混合在一起,所以如何嵌套也就非常的讲究。例如,设计一个循环结构,结构内的循环语句只有在特定的条件下才被执行,

Javascriptvar a = true;
for (var i = 0; i < 10; i++) {
    if (a) {} //条件判断
};

在这个循环里面,if语句会被反复的运行,如果这个if语句是一个固定的条件检测表达式,如果if的条件不会受到循环结构的影响,那么用下面这种结构设计会更加复合:

Javascriptif (a) {
    for (var i = 0; i < 10; i++) {
    };
}

但是if条件表达式受循环结构的制约,那就不能使用这种结构嵌套了。

避免不必要的重复操作

在循环里面经常会存在一些不必要的损耗。下面一个例子:

Javascriptfor (var i = 0; i < 10; i++) {
    var a= [1,2,3,4,5,6,7,8,9,10];
    alert(a[i]);
};

在这个循环里面,每循环一次就会在定义一个新的数组,很明显这是一个重复的操作,把数组放到循环体外面会更加的高效:

Javascriptvar a= [1,2,3,4,5,6,7,8,9,10];
for (var i = 0; i < 10; i++) {
    alert(a[i]);
};

妥善定义循环变量

对于for循环来说,它主要利用循环变量来控制整个结构的运行。当循环变量仅用于结构内部的时候,不妨在for语句外面定义循环的变量,这样也可以优化循环结构。
文章脑图

最后,如果文章有什么错误和疑问的地方,请指出。与sf各位共勉!


_我已经从中二毕业了
7.9k 声望235 粉丝

不搞前端会死星人。