我们经常需要重复操作。
例如,逐个输出列表中的商品或仅对 1 到 10 的每个数字运行相同的代码。
循环是一种多次重复相同代码的方法。
“while” 循环
while
循环具有以下语法
while (condition) {
// code
// so-called "loop body"
}
当 condition
为真时,将执行循环体中的 code
。
例如,下面的循环在 i < 3
时输出 i
let i = 0;
while (i < 3) { // shows 0, then 1, then 2
alert( i );
i++;
}
循环体的单次执行称为一次迭代。上面的示例中的循环进行三次迭代。
如果上面的示例中缺少 i++
,则循环将(理论上)永远重复。在实践中,浏览器提供了停止此类循环的方法,而在服务器端 JavaScript 中,我们可以终止进程。
任何表达式或变量都可以作为循环条件,而不仅仅是比较:条件由 while
评估并转换为布尔值。
例如,编写 while (i != 0)
的较短方法是 while (i)
let i = 3;
while (i) { // when i becomes 0, the condition becomes falsy, and the loop stops
alert( i );
i--;
}
如果循环体只有一个语句,我们可以省略大括号 {…}
let i = 3;
while (i) alert(i--);
“do…while” 循环
可以使用 do..while
语法将条件检查移动到循环体下方
do {
// loop body
} while (condition);
循环将首先执行主体,然后检查条件,并且在为真时一遍又一遍地执行它。
例如
let i = 0;
do {
alert( i );
i++;
} while (i < 3);
此语法形式仅应在您希望循环体执行至少一次而不管条件是否为真时使用。通常,首选另一种形式:while(…) {…}
。
“for” 循环
for
循环更复杂,但它也是最常用的循环。
它看起来像这样
for (begin; condition; step) {
// ... loop body ...
}
让我们通过示例了解这些部分的含义。下面的循环为 i
从 0
到(但不包括)3
运行 alert(i)
for (let i = 0; i < 3; i++) { // shows 0, then 1, then 2
alert(i);
}
让我们逐部分检查 for
语句
部分 | ||
---|---|---|
开始 | let i = 0 |
进入循环时执行一次。 |
条件 | i < 3 |
在每次循环迭代之前检查。如果为假,则循环停止。 |
主体 | alert(i) |
在条件为真时一遍又一遍地运行。 |
步骤 | i++ |
在每次迭代中主体之后执行。 |
通用循环算法的工作原理如下
Run begin
→ (if condition → run body and run step)
→ (if condition → run body and run step)
→ (if condition → run body and run step)
→ ...
即,begin
执行一次,然后它进行迭代:在每次 condition
测试后,执行 body
和 step
。
如果您是循环的新手,可以返回示例并逐步在纸上重现其运行方式,这可能会有所帮助。
以下是我们在这种情况下发生的情况
// for (let i = 0; i < 3; i++) alert(i)
// run begin
let i = 0
// if condition → run body and run step
if (i < 3) { alert(i); i++ }
// if condition → run body and run step
if (i < 3) { alert(i); i++ }
// if condition → run body and run step
if (i < 3) { alert(i); i++ }
// ...finish, because now i == 3
在此处,在循环中直接声明“计数器”变量 i
。这称为“内联”变量声明。此类变量仅在循环内可见。
for (let i = 0; i < 3; i++) {
alert(i); // 0, 1, 2
}
alert(i); // error, no such variable
我们可以使用现有变量,而不是定义变量
let i = 0;
for (i = 0; i < 3; i++) { // use an existing variable
alert(i); // 0, 1, 2
}
alert(i); // 3, visible, because declared outside of the loop
跳过部分
for
的任何部分都可以跳过。
例如,如果我们不需要在循环开始时执行任何操作,我们可以省略 begin
。
如下所示
let i = 0; // we have i already declared and assigned
for (; i < 3; i++) { // no need for "begin"
alert( i ); // 0, 1, 2
}
我们还可以删除 step
部分
let i = 0;
for (; i < 3;) {
alert( i++ );
}
这使得循环与 while (i < 3)
相同。
实际上我们可以删除所有内容,从而创建一个无限循环
for (;;) {
// repeats without limits
}
请注意,必须存在两个 for
分号 ;
。否则,会出现语法错误。
中断循环
通常,当循环条件变为假时,循环会退出。
但我们可以随时使用特殊 break
指令强制退出。
例如,下面的循环要求用户输入一系列数字,在未输入数字时“中断”
let sum = 0;
while (true) {
let value = +prompt("Enter a number", '');
if (!value) break; // (*)
sum += value;
}
alert( 'Sum: ' + sum );
如果用户输入空行或取消输入,则在行 (*)
处激活 break
指令。它立即停止循环,将控制权传递给循环后的第一行。即 alert
。
“无限循环 + 根据需要 break
”组合非常适合在循环条件必须在循环的中间甚至其正文的几个位置(而不是在循环的开头或结尾)进行检查的情况下。
继续进行下一次迭代
continue
指令是 break
的“精简版”。它不会停止整个循环。相反,它停止当前迭代并强制循环开始新的迭代(如果条件允许)。
如果我们完成了当前迭代并希望继续进行下一次迭代,我们可以使用它。
下面的循环使用 continue
仅输出奇数值
for (let i = 0; i < 10; i++) {
// if true, skip the remaining part of the body
if (i % 2 == 0) continue;
alert(i); // 1, then 3, 5, 7, 9
}
对于 i
的偶数值,continue
指令会停止执行主体,并将控制权传递给 for
的下一次迭代(使用下一个数字)。因此,alert
仅对奇数值调用。
continue
指令有助于减少嵌套显示奇值的循环可能如下所示
for (let i = 0; i < 10; i++) {
if (i % 2) {
alert( i );
}
}
从技术角度来看,这与上面的示例相同。当然,我们可以使用 if
块包装代码,而不是使用 continue
。
但作为一个副作用,这创建了另一个级别的嵌套(大括号内的 alert
调用)。如果 if
中的代码长度超过几行,则可能会降低整体可读性。
break/continue
到“?”的右侧请注意,不能将不是表达式的语法结构与三元运算符 ?
一起使用。特别是,诸如 break/continue
的指令不允许在那里使用。
例如,如果我们采用此代码
if (i > 5) {
alert(i);
} else {
continue;
}
…并使用问号重写它
(i > 5) ? alert(i) : continue; // continue isn't allowed here
…它停止工作:出现语法错误。
这是另一个不使用问号运算符 ?
而不是 if
的原因。
break/continue 的标签
有时我们需要一次从多个嵌套循环中跳出。
例如,在下面的代码中,我们循环遍历 i
和 j
,提示从 (0,0)
到 (2,2)
的坐标 (i, j)
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Value at coords (${i},${j})`, '');
// what if we want to exit from here to Done (below)?
}
}
alert('Done!');
我们需要一种方法来停止进程,如果用户取消输入。
在 input
之后的普通 break
只能中断内部循环。这还不够——标签,来救援!
标签是循环前带冒号的标识符
labelName: for (...) {
...
}
下面循环中的 break <labelName>
语句会中断到标签
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Value at coords (${i},${j})`, '');
// if an empty string or canceled, then break out of both loops
if (!input) break outer; // (*)
// do something with the value...
}
}
alert('Done!');
在上面的代码中,break outer
向上查找名为 outer
的标签,并中断该循环。
因此,控制权直接从 (*)
转到 alert('Done!')
。
我们也可以将标签移动到单独的行上
outer:
for (let i = 0; i < 3; i++) { ... }
continue
指令也可以与标签一起使用。在这种情况下,代码执行将跳转到标记循环的下一个迭代。
标签不允许我们跳转到代码中的任意位置。
例如,不可能执行此操作
break label; // jump to the label below (doesn't work)
label: for (...)
break
指令必须位于代码块内。从技术上讲,任何带标签的代码块都可以,例如
label: {
// ...
break label; // works
// ...
}
…尽管 99.9% 的情况下,break
用于循环内,如我们在上面的示例中所见。
continue
只能从循环内执行。
总结
我们介绍了 3 种类型的循环
while
– 在每次迭代之前检查条件。do..while
– 在每次迭代之后检查条件。for (;;)
– 在每次迭代之前检查条件,提供其他设置。
为了形成“无限”循环,通常使用 while(true)
构造。这样的循环与任何其他循环一样,都可以使用 break
指令停止。
如果我们不想在当前迭代中执行任何操作,并且希望转发到下一个迭代,我们可以使用 continue
指令。
break/continue
支持循环前的标签。标签是 break/continue
逃逸嵌套循环以转到外部循环的唯一方式。
评论
<code>
标记,对于多行 - 将它们包装在<pre>
标记中,对于超过 10 行 - 使用沙盒 (plnkr、jsbin、codepen…)