有时候我可能需要重复执行很多代码,比如执行一万句输出语句,我当然可以一句一句敲,敲一万行代码,但是这显然很呆。然而这样重复完成一件事,在程序中是很常见的需求,所以各种编程语言都提供了循环语句,用于实现这种重复运行一段代码的功能。
JS 中提供了几种循环结构,这里只介绍三个循环语句,分别是 for 循环,while 循环, do-while 循环,这三种循环也是最常见的三种循环,其中最常用的是 for 循环,所以重点学习,而 while 循环和 do-whild 循环则非常简单。
首先先把最简单的两个循环介绍一下,分别是 while 循环和 do-whild 循环。
whild 循环其实类似 if 语句
js
if(grade >= 60){
console.log("你及格了");
}
while(grade >= 60){
console.log("你及格了");
}可以看到,语法结构都是一样的,while 和 if 的区别在于,if 语句如果表达式为真,只会执行一次代码块,而 while 循环只要表达式为真,则会一直重复执行代码块。很显然,只要 grade 大于等于 60,那么 while 循环会一直执行输出语句,永远不会停息,这种永不停息的循环,我们称为死循环,这就是一个典型的死循环,这种死循环会导致页面卡死,所以我们应该尽量避免,一般来说,出现页面卡死的情况,大概率也是因为死循环导致的。
并不是所有死循环都是不好的,这一点需要深入学习之后读者才会明白。
接下来看一个正常一点的例子,输出 1 得到 1000
js
var i = 1;
while(i <= 1000){
console.log(i);
i++;
}在这个例子中,只要 i 小于等于 1000,就会重复执行代码块里面的代码,但是这个例子并不会导致死循环,因为在代码块内部,i 的值是会不断增加的,所以当 i 的值增加到 1001 之后,此时 i<=1000 就为 false 了,就不会再执行循环内部的代码,也就跳出了循环,我们可以看到,第一次进入循环的时候,i 的值为 1,而最后一次进入循环的时候,i 的值为 1000,所以这个代码就可以正确输出 1 到 1000。
while 循环内部的代码块我们也称为循环体
可以看到,循环的本质,就是判断括号里面的表达式,如果为真,就会一直执行内部的代码,这很好理解。
接下来是 do-while 循环,do-while 循环和 while 循环类似,但有一些区别。
js
var i = 1;
while(i <= 100){
console.log(i);
i++;
}
var j = 1;
do{
console.log(i);
j++;
}while(j <= 100);上面代码中,第一部分为已经介绍过的 while 循环,相信读者已经可以理解这是什么意思。下面的就是 do-while 循环。显然,while 循环和 do-while 循环的一个显而易见的区别就是写法不同,do-while 循环的写法可以参照上面的代码,我们发现好像就是把循环体放到前面去了,同时最开始加上了一个 do。不过需要注意的是,do-while 循环最后是需要添加分号的。
do-while 循环和 while 循环一样,也是条件满足就执行循环体,而对于上面的 while 循环和 do-while 循环,效果其实是完全一样的。接下来我们看一下他们的区别
js
while(false){
console.log("while");
}
do{
console.log("do-while")
}while(false);上面的代码会输出一次 do-while,这就体现出了 while 和 do-while 的区别,在 while 循环中,先对括号里面进行布尔判定,在上例中,布尔判定为 false,所以并不执行循环体的内容。而对 do-while 而言,他会先执行一遍循环体,再进行布尔判定,如果布尔判定为真,就会继续循环,否则就会跳出循环。
很显然, while 循环和 do-while 循环本质上就只有一个区别,do-while 循环不论如何都会先运行一次循环体,是运行完一次循环体之后做布尔判定,而 while 循环则是在最开始就进行布尔判定,循环体有可能一次都不运行。
接下来是 for 循环,for 循环是用的最常用的循环。for 循环的语法结构如下。
js
for(表达式1;表达式2;表达式3){
循环体
}可以看到,for 循环和 while 循环的区别就在于,while 循环括号里面只能填入一个表达式,而 for 循环可以用分号隔开填入三个表达式,我们可以用流程图来表示 for循环的执行过程。
mermaid
graph TD
begin((开始))---->bds1[执行表达式1]
bds1---->bds2{表达式2是否为真}
bds2--Y-->xht[执行循环体]---->bds3[执行表达式3]---->bds2
bds2--N-->ed((结束))可以看到,对于 for 循环而言,在循环之前,一定会运行一次表达式 1,然后对表达式 2 进行布尔判定,如果布尔判定为真,就会执行循环体,每次执行完循环体后,还会执行一次表达式 3。
有的读者可能会觉得这个规则奇奇怪怪的,我们来考虑一个场景,我们要输出 1 到 100,分别用 while 循环和 for 循环实现
js
//while循环
var i = 1;
while(i <= 100){
console.log(i);
i++;
}
//for循环
for(var j = 1;j<=100;j++){
console.log(j)
}以上两个循环代码是完全等价的,如果我们循环固定次数,使用 while 循环则需要初始化一个变量,然后每次循环体结束还要改变变量,这样很麻烦。所以对于固定次数的循环,我们更推荐使用 for 循环。for 循环的第一个表达式通常都是用来初始化一个循环变量,第二个表达式则和 while 语句中的表达式一样,都是用来做布尔判定的。第三个表达式通常是对循环变量进行修改。这样就可以把循环变量全部放到 for 循环的括号中去,不用像 while 循环那样再循环外单独定义循环变量。
值得注意的是,对于 for 循环而言,表达式 1 和表达式 3 都可以省略,如下
js
var i = 1;
for(;i<=100;){
console.log(i);
i++;
}很显然,对于这种省略表达式 1 和表达式 3 的 for 循环,就等价于一个 while 循环。
接下来我们考虑另一个需求,就是输出 100 到 1 的所有数字
js
for(var i = 100;i>0;i--){
console.log(i);
}我们只需要将循环变量初始化为 100,然后每次减一,即可实现这样的需求。
接下来是输出 1 到 100 的所有奇数
js
for(var i = 1;i<=100;i++){
if(i % 2 == 1)
console.log(i);
}显然,对 2 取余为 1 的数字,就是奇数,所以我们只需要在输出之前判断一下,如果这个数是奇数,就输出。
当然,由于奇数加二依然是奇数,所以有的读者可能会有另一种思路来实现这样的需求,这也是一种很好的方式
js
for(var i = 1;i<=100;i+=2){
console.log(i);
}在循环中,我们有两个控制语句,分别是 break 和 continue,break 表示跳出循环,或者说终止循环,continue 表示跳过本次循环。
我们先介绍 break,break 其实我们都介绍过,break 不仅可以用于跳出 switch,也可以用于跳出循环。
js
for(var i = 1;i<=100;i++){
if(i == 50){
break;
}
console.log(i);
}以上面的代码为例,i 从 1 循环到 100,每次循环输出 i,但是在输出 i 之前,要先进行判断,如果 i 的值为 50,就终止循环。此时在控制台上,我们可以看到从 1 输出到 49,因为当第 50 次循环的时候,由于 i 为 50,此时会执行 break 语句,此时这个循环就被强制停止了,不会再继续执行了。
如果我们把 break 换成 continue
js
for(var i = 1;i<=100;i++){
if(i == 50){
continue;
}
console.log(i);
}此时也是从 1 输出到 100,但是并不会输出 50,看到区别了吗,当第 50 次循环,i 等于 50,此时运行了 continue,只会跳过本次循环,紧接着执行下一次循环,也就是跳到表达式 3 那儿执行。可见,continue 只会跳过本次循环,而 break 可以直接终止整个循环。
接下来考虑几个常见场景,以便读者了解循环应该如何应用。
1 到 100 累加:
js
var result = 0;
for(var i = 1;i<=100;i++){
result += i;
}
console.log(result);1-10 累乘
js
var result = 1;
for(var i = 1;i<=10;i++){
result *= i;
}
console.log(result);1-100 所有奇数相加
js
var result = 0;
for(var i = 1;i<100;i++){
if(i%2 == 0)//如果是偶数,跳过本次循环,当然,这里有很多其他写法,这里这样写单纯是练习continue
continue;
result += i;
}
console.log(result);以上三个案例可以归类为累积问题
找出 1 到 1000 中所有能整除 24 的数字
js
for(var i = 1;i<100;i++){
if(i%24 == 0)
console.log(i);
}判断一个数是否是素数
js
var num = 114;
for(var i = 2;i<num;i++){
if(num % i == 0)
break;
}
if(i == num){
console.log("是素数");
}else{
console.log("不是素数");
}这个代码有必要进一步解释,首先,这里判断的是 114 是否为素数,当然,把 num 改成几,就是判断几是不是素数。首先,循环内部先依次遍历 1 到 114 所有数字,看看是否能被 114 整除,如果其中存在一个数可以被 114 整除,那么说明这不是素数。循环结束后,有两种情况,第一种情况就是循环正常结束,在这种情况下,最后 i 的数值就是 114,如果 i 的值为 114,说明循环不是被 break 终止的,也就是说这个数是素数。而还有一种情况,就是循环被 break 终止,当被 break 终止的时候,说明 114 至少可以整除一个 i,所以就不是素数。
输出 1-100 的所有素数
js
for(var i = 1;i<=100;i++){
for(var j = 2;j<i;j++){
if(i % j == 0)
break;
}
if(j == i)
console.log(i);
}