我们经常需要重复动作。
例如,从一个列表中一个接一个地输出商品,或者只是对从 1 到 10 的每个数字运行相同的代码。
循环是多次重复相同代码的一种方法。
for...of 和 for...in 循环
给高级读者的一个小公告。
本文仅介绍基本循环: while 、 do..while和for(..;..;..) 。
如果您阅读本文是为了寻找其他类型的循环,请参考以下内容:
请参阅 for...in 来循环对象属性。
有关数组和可迭代对象的循环,请参阅 for...of 和 iterables。
否则,请继续阅读。
while循环具有以下语法:
while(条件){
// 代码
// 所谓的“循环体”
}当condition为真时,将执行循环体中的code 。
例如,下面的循环在i < 3时输出i :
让我= 0;
while (i < 3) { // 显示 0,然后是 1,然后是 2
警报(我);
我++;
}循环体的单次执行称为迭代。上例中的循环进行了 3 次迭代。
如果上面的示例中缺少i++ ,则循环将(理论上)永远重复。实际上,浏览器提供了停止此类循环的方法,并且在服务器端 JavaScript 中,我们可以终止该进程。
任何表达式或变量都可以是循环条件,而不仅仅是比较:条件由while计算并转换为布尔值。
例如,编写while (i != 0)的更短方法是while (i) :
让我= 3;
while (i) { // 当 i 变为 0 时,条件变为假,循环停止
警报(我);
我 - ;
}单行主体不需要花括号
如果循环体只有一条语句,我们可以省略大括号{…} :
让我= 3; 同时(i)警报(i--);
可以使用do..while语法将条件检查移至循环体下方:
做 {
// 循环体
while(条件);循环将首先执行主体,然后检查条件,当条件为真时,一次又一次地执行它。
例如:
让我= 0;
做 {
警报(我);
我++;
而(i<3);仅当您希望循环体至少执行一次而无论条件是否为真时,才应使用这种形式的语法。通常,首选其他形式: while(…) {…} 。
for循环更复杂,但它也是最常用的循环。
它看起来像这样:
for(开始;条件;步骤){
// ...循环体...
}让我们通过例子来了解这些部分的含义。下面的循环为i从0到(但不包括) 3运行alert(i) :
for (let i = 0; i < 3; i++) { // 显示 0,然后是 1,然后是 2
警报(一);
}让我们逐个检查for语句:
| 部分 | ||
|---|---|---|
| 开始 | let i = 0 | 进入循环后执行一次。 |
| 状况 | i < 3 | 在每次循环迭代之前检查。如果为 false,则循环停止。 |
| 身体 | alert(i) | 当条件为真时,一次又一次地运行。 |
| 步 | i++ | 在每次迭代的主体之后执行。 |
一般循环算法的工作原理如下:
运行开始 →(如果条件→运行主体并运行步骤) →(如果条件→运行主体并运行步骤) →(如果条件→运行主体并运行步骤) → ...
也就是说, begin执行一次,然后迭代:在每个condition测试之后,执行body和step 。
如果您对循环不熟悉,那么返回示例并在纸上重现它的逐步运行方式可能会有所帮助。
这正是我们案例中发生的情况:
// for (让 i = 0; i < 3; i++) 警报(i)
// 运行开始
让我= 0
// if 条件 → 运行主体和运行步骤
if (i < 3) { 警报(i);我++ }
// if 条件 → 运行主体和运行步骤
if (i < 3) { 警报(i);我++ }
// if 条件 → 运行主体和运行步骤
if (i < 3) { 警报(i);我++ }
// ...完成,因为现在 i == 3内联变量声明
这里,“计数器”变量i是在循环中声明的。这称为“内联”变量声明。此类变量仅在循环内部可见。
for (令 i = 0; i < 3; i++) {
警报(一); // 0, 1, 2
}
警报(一); // 错误,没有这样的变量我们可以使用现有的变量,而不是定义变量:
让我= 0;
for (i = 0; i < 3; i++) { // 使用现有变量
警报(一); // 0, 1, 2
}
警报(一); // 3、可见,因为在循环外声明for的任何部分都可以被跳过。
例如,如果我们不需要在循环开始时执行任何操作,则可以省略begin 。
就像这里:
让我= 0; // 我们已经声明并分配了 i
for (; i < 3; i++) { // 不需要“开始”
警报(我); // 0, 1, 2
}我们还可以删除step部分:
让我= 0;
对于 (; i < 3;) {
警报(i++);
}这使得循环与while (i < 3)相同。
我们实际上可以删除所有内容,创建一个无限循环:
为了 (;;) {
// 无限重复
}请注意,两个for分号;必须在场。否则,就会出现语法错误。
通常,当条件变为假时,循环就会退出。
但我们可以使用特殊的break指令随时强制退出。
例如,下面的循环要求用户输入一系列数字,当没有输入数字时“中断”:
令总和 = 0;
而(真){
let value = +prompt("请输入一个数字", '');
if (!value) 中断; //(*)
总和+=值;
}
警报('总和:'+总和);如果用户输入空行或取消输入,则在行(*)处激活break指令。它立即停止循环,将控制权传递给循环后的第一行。即, alert 。
“无限循环 + 根据需要break ”的组合非常适合必须在循环的中间甚至循环体的多个位置而不是在循环的开头或结尾检查循环条件的情况。
continue指令是break的“更轻版本”。它不会停止整个循环。相反,它会停止当前迭代并强制循环开始新的迭代(如果条件允许)。
如果我们完成了当前的迭代并且想要继续下一个迭代,我们可以使用它。
下面的循环使用continue仅输出奇数:
for (令 i = 0; i < 10; i++) {
// 如果为 true,则跳过正文的剩余部分
if (i % 2 == 0) 继续;
警报(一); // 1,然后是 3, 5, 7, 9
}对于偶数i值, continue指令停止执行主体并将控制传递给for的下一次迭代(使用下一个数字)。因此,只有奇数值才会发出alert 。
continue指令有助于减少嵌套
显示奇数值的循环可能如下所示:
for (令 i = 0; i < 10; i++) {
如果(我%2){
警报(我);
}
}从技术角度来看,这与上面的示例相同。当然,我们可以将代码包装在if块中,而不是使用continue 。
但副作用是,这又创建了一层嵌套(大括号内的alert调用)。如果if内部的代码超过几行,则可能会降低整体可读性。
没有break/continue到“?”的右侧
请注意,不是表达式的语法结构不能与三元运算符?一起使用。 。特别是,那里不允许使用诸如break/continue类的指令。
例如,如果我们采用以下代码:
如果(我> 5){
警报(一);
} 别的 {
继续;
}…并用问号重写它:
(i > 5) ?警报(i):继续; // 这里不允许继续
...它停止工作:存在语法错误。
这只是不使用问号运算符的另一个原因?而不是if 。
有时我们需要立即跳出多个嵌套循环。
例如,在下面的代码中,我们循环遍历i和j ,提示输入从(0,0)到(2,2)坐标(i, j) ):
for (令 i = 0; i < 3; i++) {
for (令 j = 0; j < 3; j++) {
let input =提示(`坐标处的值(${i},${j})`, '');
// 如果我们想从这里退出到完成(如下)怎么办?
}
}
警报('完成!');如果用户取消输入,我们需要一种方法来停止该过程。
input后的普通break只会中断内部循环。这还不够——标签来拯救你!
标签是一个在循环之前带有冒号的标识符:
标签名称: for (...) {
...
}下面循环中的break <labelName>语句中断到标签:
外部: for (让 i = 0; i < 3; i++) {
for (令 j = 0; j < 3; j++) {
let input =提示(`坐标处的值(${i},${j})`, '');
// 如果为空字符串或已取消,则跳出两个循环
if (!input) 中断外部; //(*)
// 对值做一些事情...
}
}
警报('完成!');在上面的代码中, break outer向上查找名为outer标签并跳出该循环。
因此控制直接从(*)转到alert('Done!') 。
我们还可以将标签移动到单独的行上:
外:
for (令 i = 0; i < 3; i++) { ... } continue指令也可以与标签一起使用。在这种情况下,代码执行跳转到标记循环的下一次迭代。
标签不允许“跳”到任何地方
标签不允许我们跳转到代码中的任意位置。
例如,不可能这样做:
打破标签; // 跳转到下面的标签(不起作用) 标签: 用于 (...)
break指令必须位于代码块内。从技术上讲,任何带标签的代码块都可以,例如:
标签: {
// ...
打破标签; // 作品
// ...
} …尽管如此,99.9% 的时间break都在循环内使用,正如我们在上面的示例中所看到的。
只能从循环内部continue 。
我们介绍了 3 种类型的循环:
while – 在每次迭代之前检查条件。
do..while – 每次迭代后都会检查条件。
for (;;) – 在每次迭代之前检查条件,可以使用其他设置。
为了创建“无限”循环,通常使用while(true)结构。这样的循环,就像任何其他循环一样,可以使用break指令停止。
如果我们不想在当前迭代中执行任何操作并希望前进到下一个迭代,则可以使用continue指令。
循环之前的break/continue支持标签。标签是break/continue跳出嵌套循环并转到外部循环的唯一方法。
重要性:3
此代码警报的最后一个值是什么?为什么?
让我= 3;
而(一){
警报(我--);
}答案: 1 .
让我= 3;
而(一){
警报(我--);
}每次循环迭代都会将i减少1 。当i = 0时,检查while(i)停止循环。
因此,循环的步骤形成以下序列(“循环展开”):
让我= 3; 警报(我--); // 显示 3,将 i 减少到 2 Alert(i--) // 显示 2,将 i 减少到 1 Alert(i--) // 显示 1,将 i 减少到 0 // 完成,while(i) 检查停止循环
重要性:4
对于每次循环迭代,记下其输出的值,然后将其与解决方案进行比较。
两个循环是否alert相同的值?
前缀形式++i :
让我= 0; while (++i < 5) 警报( i );
后缀形式i++
让我= 0; while (i++ < 5) 警报( i );
该任务演示了在比较中使用后缀/前缀形式如何导致不同的结果。
从 1 到 4
让我= 0; while (++i < 5) 警报( i );
第一个值是i = 1 ,因为++i首先递增i ,然后返回新值。因此第一个比较是1 < 5并且alert显示1 。
然后按照2, 3, 4… ——数值依次出现。比较始终使用递增的值,因为++位于变量之前。
最后, i = 4增加到5 ,比较while(5 < 5)失败,循环停止。所以没有显示5 。
从 1 到 5
让我= 0; while (i++ < 5) 警报( i );
第一个值仍然是i = 1 。 i++的后缀形式将i递增,然后返回旧值,因此比较i++ < 5将使用i = 0 (与++i < 5相反)。
但alert呼叫是单独的。这是在增量和比较之后执行的另一条语句。所以它得到当前i = 1 。
然后按照2, 3, 4…
让我们在i = 4处停止。前缀形式++i会增加它并在比较中使用5 。但这里我们有后缀形式i++ 。因此它将i增加到5 ,但返回旧值。因此,比较实际上是while(4 < 5) – true,并且控件继续发出alert 。
值i = 5是最后一个,因为在下一步中while(5 < 5)为 false。
重要性:4
对于每个循环,写下它将显示哪些值。然后与答案进行比较。
两个循环是否alert相同的值?
后缀形式:
for (令 i = 0; i < 5; i++) 警报 ( i );
前缀形式:
for (令 i = 0; i < 5; ++i) 警报 ( i );
答案:两种情况都是从0到4 。
for (令 i = 0; i < 5; ++i) 警报 ( i ); for (令 i = 0; i < 5; i++) 警报 ( i );
这可以很容易地从for的算法中推导出来:
在一切(开始)之前执行一次i = 0 。
检查条件i < 5
如果为true – 执行循环体alert(i) ,然后执行i++
增量i++与条件检查 (2) 分开。这只是另一种说法。
这里不使用增量返回的值,因此i++和++i之间没有区别。
重要性:5
使用for循环输出2到10之间的偶数。
运行演示
for (令 i = 2; i <= 10; i++) {
如果(我%2==0){
警报(我);
}
}我们使用“模”运算符%来获取余数并检查此处的均匀性。
重要性:5
重写代码,将for循环更改为while而不改变其行为(输出应保持不变)。
for (令 i = 0; i < 3; i++) {
警报(`数字$ {i}!`);
}让我= 0;
而(我<3){
警报(`数字$ {i}!`);
我++;
}重要性:5
编写一个循环,提示输入大于100数字。如果访客输入另一个号码 - 请他们再次输入。
循环必须要求输入数字,直到访问者输入大于100的数字或取消输入/输入空行。
这里我们可以假设访问者只输入数字。在此任务中无需对非数字输入进行特殊处理。
运行演示
让数字;
做 {
num =提示("请输入大于100的数字?", 0);
while (num <= 100 && num);当两个检查都为真时,循环do..while会重复:
检查num <= 100 – 即输入的值仍然不大于100 。
当num为null或空字符串时,检查&& num为 false。然后while循环也停止。
PS 如果num为null则num <= 100为true ,因此如果没有第二次检查,如果用户单击“取消”,循环将不会停止。两项检查都是必需的。
重要性:3
大于1整数如果被除1和它本身以外的任何数除而无余数,则称为素数。
换句话说,如果 n > 1 不能被除1和n之外的任何数整除,则n > 1是素数。
例如, 5是素数,因为它不能除以2 、 3和4且无余数。
编写输出2到n区间内素数的代码。
对于n = 10结果将为2,3,5,7 。
PS 该代码应该适用于任何n ,而不是针对任何固定值进行硬调整。
有很多算法可以完成这项任务。
让我们使用嵌套循环:
对于区间 { 中的每个 i
检查 i 是否有 1..i 的除数
如果是=>该值不是素数
如果否=>该值为素数,则显示它
}使用标签的代码:
令 n = 10;
下一个总理:
for (let i = 2; i <= n; i++) { // 对于每个 i...
for (let j = 2; j < i; j++) { // 寻找除数..
if (i % j == 0) 继续 nextPrime; // 不是素数,继续下一个 i
}
警报(我); // 素数
}有很大的优化空间。例如,我们可以查找从2到i的平方根的除数。但无论如何,如果我们想要在大区间内真正高效,我们需要改变方法并依赖高级数学和复杂算法,如二次筛、通用数域筛等。