循环和迭代
循环提供了一种快速简便的方法来重复执行某些操作。本节介绍 JavaScript 指南中可用的不同迭代语句。
您可以将循环视为计算机化的游戏版本,您告诉某人向一个方向走 *X* 步,然后向另一个方向走 *Y* 步。例如,想法“向东走五步”可以用循环这样表达
for (let step = 0; step < 5; step++) {
// Runs 5 times, with values of step 0 through 4.
console.log("Walking east one step");
}
循环有很多种,但它们基本上都做同样的事情:重复执行某个操作若干次。(注意,这个数字可能是零!)
各种循环机制提供了不同的方法来确定循环的起点和终点。有各种情况更容易通过一种类型的循环而不是其他类型来处理。
JavaScript 提供的循环语句是
for 语句
一个 for
循环会一直重复执行,直到指定条件计算结果为 false。JavaScript for
循环类似于 Java 和 C for
循环。
for
语句的格式如下
for (initialization; condition; afterthought)
statement
当 for
循环执行时,会发生以下情况
- 执行初始化表达式
initialization
(如果有)。此表达式通常初始化一个或多个循环计数器,但语法允许任意复杂度的表达式。此表达式还可以声明变量。 - 计算
condition
表达式。如果condition
的值为 true,则执行循环语句。否则,for
循环终止。(如果完全省略condition
表达式,则假定条件为 true。) - 执行
statement
。要执行多个语句,请使用 块语句 ({ }
) 对这些语句进行分组。 - 如果存在,则执行更新表达式
afterthought
。 - 控制返回步骤 2。
示例
在下面的示例中,函数包含一个 for
语句,该语句计算滚动列表中所选选项的数量(允许进行多项选择的 <select>
元素)。
HTML
<form name="selectForm">
<label for="musicTypes"
>Choose some music types, then click the button below:</label
>
<select id="musicTypes" name="musicTypes" multiple>
<option selected>R&B</option>
<option>Jazz</option>
<option>Blues</option>
<option>New Age</option>
<option>Classical</option>
<option>Opera</option>
</select>
<button id="btn" type="button">How many are selected?</button>
</form>
JavaScript
这里,for
语句声明变量 i
并将其初始化为 0
。它检查 i
是否小于 <select>
元素中的选项数量,执行随后的 if
语句,并在每次通过循环后将 i
增加 1。
function countSelected(selectObject) {
let numberSelected = 0;
for (let i = 0; i < selectObject.options.length; i++) {
if (selectObject.options[i].selected) {
numberSelected++;
}
}
return numberSelected;
}
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
const musicTypes = document.selectForm.musicTypes;
console.log(`You have selected ${countSelected(musicTypes)} option(s).`);
});
do...while 语句
do...while
语句会重复执行,直到指定条件计算结果为 false。
do...while
语句的格式如下
do
statement
while (condition);
statement
始终在检查条件之前执行一次。(要执行多个语句,请使用块语句 ({ }
) 对这些语句进行分组。)
如果 condition
为 true
,则再次执行语句。在每次执行结束时,都会检查条件。当条件为 false
时,执行停止,控制权传递到 do...while
之后的语句。
示例
在以下示例中,do
循环至少迭代一次,并重复迭代,直到 i
不再小于 5
。
let i = 0;
do {
i += 1;
console.log(i);
} while (i < 5);
while 语句
while
语句会执行其语句,只要指定条件计算结果为 true
。while
语句的格式如下
while (condition)
statement
如果 condition
变成 false
,则循环内的 statement
停止执行,控制权传递到循环之后的语句。
条件测试发生在循环中执行 statement
*之前*。如果条件返回 true
,则执行 statement
并再次测试 condition
。如果条件返回 false
,则执行停止,控制权传递到 while
之后的语句。
要执行多个语句,请使用块语句 ({ }
) 对这些语句进行分组。
示例 1
以下 while
循环会一直迭代,只要 n
小于 3
let n = 0;
let x = 0;
while (n < 3) {
n++;
x += n;
}
每次迭代时,循环都会增加 n
并将该值添加到 x
。因此,x
和 n
会获得以下值
- 第一次通过后:
n
=1
且x
=1
- 第二次通过后:
n
=2
且x
=3
- 第三次通过后:
n
=3
且x
=6
完成第三次通过后,条件 n < 3
不再为 true
,因此循环终止。
示例 2
避免无限循环。确保循环中的条件最终变成 false
——否则,循环将永远不会终止!以下 while
循环中的语句会永远执行,因为条件永远不会变成 false
// Infinite loops are bad!
while (true) {
console.log("Hello, world!");
}
带标签的语句
label
为语句提供一个标识符,让您可以在程序中的其他位置引用该标识符。例如,您可以使用标签来标识循环,然后使用 break
或 continue
语句来指示程序是否应该中断循环或继续执行。
带标签语句的语法如下
label:
statement
label
的值可以是任何 JavaScript 标识符,但不能是保留字。您使用标签标识的 statement
可以是任何语句。有关使用带标签语句的示例,请参阅下面的 break
和 continue
示例。
break 语句
使用 break
语句来终止循环、switch
或与带标签语句一起使用。
- 当您在没有标签的情况下使用
break
时,它会立即终止最内层的封闭while
、do-while
、for
或switch
,并将控制权转移到接下来的语句。 - 当您在有标签的情况下使用
break
时,它会终止指定的带标签语句。
break
语句的语法如下
break;
break label;
- 语法的第一种形式会终止最内层的封闭循环或
switch
。 - 语法的第二种形式会终止指定的封闭带标签语句。
示例 1
以下示例迭代数组中的元素,直到找到值为 theValue
的元素的索引
for (let i = 0; i < a.length; i++) {
if (a[i] === theValue) {
break;
}
}
示例 2:跳到标签
let x = 0;
let z = 0;
labelCancelLoops: while (true) {
console.log("Outer loops:", x);
x += 1;
z = 1;
while (true) {
console.log("Inner loops:", z);
z += 1;
if (z === 10 && x === 10) {
break labelCancelLoops;
} else if (z === 10) {
break;
}
}
}
continue 语句
continue
语句可以用来重新启动 while
、do-while
、for
或 label
语句。
- 当您在没有标签的情况下使用
continue
时,它会终止最内层的封闭while
、do-while
或for
语句的当前迭代,并继续执行循环的下一个迭代。与break
语句不同,continue
不会完全终止循环的执行。在while
循环中,它会跳回到条件。在for
循环中,它会跳到increment-expression
。 - 当您在有标签的情况下使用
continue
时,它适用于使用该标签标识的循环语句。
continue
语句的语法如下
continue;
continue label;
示例 1
以下示例显示了一个带有 continue
语句的 while
循环,该语句在 i
的值为 3
时执行。因此,n
的值为 1
、3
、7
和 12
。
let i = 0;
let n = 0;
while (i < 5) {
i++;
if (i === 3) {
continue;
}
n += i;
console.log(n);
}
// Logs:
// 1 3 7 12
如果您注释掉 continue;
,循环将运行到最后,您将看到 1,3,6,10,15
。
示例 2
一个名为checkiandj
的语句包含一个名为checkj
的语句。如果遇到continue
,程序将终止checkj
的当前迭代并开始下一个迭代。每次遇到continue
时,checkj
都会重复迭代,直到其条件返回false
。当返回false
时,checkiandj
语句的剩余部分将完成,并且checkiandj
会重复迭代,直到其条件返回false
。当返回false
时,程序将在checkiandj
之后的语句处继续执行。
如果continue
有一个名为checkiandj
的标签,程序将在checkiandj
语句的顶部继续执行。
let i = 0;
let j = 10;
checkiandj: while (i < 4) {
console.log(i);
i += 1;
checkj: while (j > 4) {
console.log(j);
j -= 1;
if (j % 2 === 0) {
continue checkj;
}
console.log(j, "is odd.");
}
console.log("i =", i);
console.log("j =", j);
}
for...in 语句
for...in
语句会将指定的变量迭代到对象的全部可枚举属性。对于每个不同的属性,JavaScript都会执行指定的语句。for...in
语句的格式如下所示:
for (variable in object)
statement
示例
下面的函数以一个对象和对象的名称作为参数。然后,它会遍历对象的所有属性,并返回一个字符串,其中列出了属性名称及其值。
function dumpProps(obj, objName) {
let result = "";
for (const i in obj) {
result += `${objName}.${i} = ${obj[i]}<br>`;
}
result += "<hr>";
return result;
}
对于一个具有make
和model
属性的car
对象,result
将是:
car.make = Ford car.model = Mustang
数组
虽然可能会想要用它来遍历Array
元素,但for...in
语句会返回用户定义属性的名称以及数值索引。
因此,在遍历数组时,最好使用传统的for
循环以及数值索引,因为for...in
语句会遍历用户定义的属性,以及数组元素,如果您修改了Array
对象(例如,添加自定义属性或方法)。
for...of 语句
for...of
语句会创建一个循环,它会遍历可迭代对象(包括Array
、Map
、Set
、arguments
对象等),并使用要为每个不同属性的值执行的语句调用自定义迭代钩子。
for (variable of iterable)
statement
下面的示例展示了for...of
循环和for...in
循环之间的区别。for...in
循环遍历属性名,而for...of
循环遍历属性值。
const arr = [3, 5, 7];
arr.foo = "hello";
for (const i in arr) {
console.log(i);
}
// "0" "1" "2" "foo"
for (const i of arr) {
console.log(i);
}
// Logs: 3 5 7
for...of
和for...in
语句也可以和解构赋值一起使用。例如,您可以使用Object.entries()
同时遍历对象的键和值。
const obj = { foo: 1, bar: 2 };
for (const [key, val] of Object.entries(obj)) {
console.log(key, val);
}
// "foo" 1
// "bar" 2