原文:JavaScript Loops Explained: For Loop, While Loop, Do...while Loop, and More

在 JavaScript 中,我们使用循环来根据条件执行重复的任务。条件通常返回 truefalse。循环将持续运行,直到定义的条件返回 false

for 循环

语法

for (initialization; condition; finalExpression) {
  // code
}

for 循环由三个可选的表达式组成,后面是一个代码块:

  • initialization - 这个表达式在执行第一个循环之前运行,通常用来创建一个计数器。
  • condition - 这个表达式每次在循环运行前都会被检查。如果它的值为 true,则执行循环中的语句或代码。如果它的值为 false,则循环停止。如果这个表达式被省略,它会自动评估为 true
  • finalExpression - 这个表达式在循环的每次迭代后执行。这通常用于增加一个计数器,但也可用于减少一个计数器。

这三个表达式中的任何一个或者代码块中的代码都可以被省略。

for 循环通常用于按设定次数运行代码。另外,在条件表达式评估为 false 之前,你可以使用 break 来提前退出循环。

示例

1. 遍历 0-8 的整数:

for (let i = 0; i < 9; i++) {
  console.log(i);
}

// Output:
// 0
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8

2. 在 conditionfalse 之前用 break 跳出一个 for 循环:

for (let i = 1; i < 10; i += 2) {
  if (i === 7) {
    break;
  }
  console.log('Total elephants: ' + i);
}

// Output:
// Total elephants: 1
// Total elephants: 3
// Total elephants: 5

常见陷阱:超出数组的界限

遍历数组时,很容易意外超出数组的界限。

例如,你的循环可能会尝试引用只有 3 个元素的数组的第 4 个元素:

const arr = [ 1, 2, 3 ];

for (let i = 0; i <= arr.length; i++) {
  console.log(arr[i]);
}

// Output:
// 1
// 2
// 3
// undefined

有两种方式修复代码:将 condition 设置为 i < arr.lengthi <= arr.length - 1

for...in 循环

语法

for (property in object) {
  // code
}

for...in 循环遍历对象的属性。对于每个属性,都会执行代码块中的代码。

示例

1. 遍历对象的属性并将其名称和值打印到控制台:

const capitals = {
  a: "Athens",
  b: "Belgrade",
  c: "Cairo"
};

for (let key in capitals) {
  console.log(key + ": " + capitals[key]);
}

// Output:
// a: Athens
// b: Belgrade
// c: Cairo

常见陷阱:在数组上进行迭代时出现的意外行为

尽管你可以使用 for...in 循环来迭代一个数组,但我们建议使用普通的 forfor...of 循环。

for...in 循环可以遍历数组和类似数组的对象,但是它不一定能按顺序访问数组索引。

另外,for...in 循环会返回数组或类数组对象的所有属性和继承属性,这可能导致意外的行为。

例如,这个简单的循环按预期运行:

const array = [1, 2, 3];

for (const i in array) {
  console.log(i);
}

// 0
// 1
// 2

但是,如果你使用的 JS 库之类的东西直接修改了 Array 原型,那么 for...in 循环也会对其进行迭代:

const array = [1, 2, 3];

Array.prototype.someMethod = true;

for (const i in array) {
  console.log(i);
}

// 0
// 1
// 2
// someMethod

尽管直接修改像 ArrayObject 这样的只读原型有悖于最佳实践,但对于某些库或代码库来说,这可能是一个问题。

另外,由于 for...in 是针对对象的,它对数组的处理要比其他循环慢得多。

简而言之,记住只用 for...in 循环来迭代对象,而不是数组。

for...of 循环

语法

for (variable of object) {
  // code
}

for...of 循环迭代许多类型的可迭代对象的值,包括数组和特殊的集合类型,如 SetMap。对于可迭代对象中的每个值,执行代码块中的代码。

示例

1. 遍历一个数组:

const arr = [ "Fred", "Tom", "Bob" ];

for (let i of arr) {
  console.log(i);
}

// Output:
// Fred
// Tom
// Bob

2. 遍历 Map

const m = new Map();
m.set(1, "black");
m.set(2, "red");

for (let n of m) {
  console.log(n);
}

// Output:
// [1, black]
// [2, red]

3. 遍历 Set

const s = new Set();
s.add(1);
s.add("red");

for (let n of s) {
  console.log(n);
}

// Output:
// 1
// red

while 循环

语法

while (condition) {
  // statement
}

while 循环从评估 condition 开始。如果 conditiontrue,则代码块中的代码将被执行。如果 conditionfalse,则不执行代码块中的代码并且循环结束。

示例

  1. 当变量小于 10 时,将其打印到控制台并加 1:
let i = 1;

while (i < 10) {
  console.log(i);
  i++;
}

// Output:
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9

do...while 循环

语法

do {
  // statement
} while (condition);

do...while 循环与 while 循环密切相关。在 do...while 循环中,在循环的每次迭代结束时检查 condition,而不是在循环开始之前检查。

这意味着 do...while 循环中的代码保证至少运行一次,即使 condition 表达式的结果已经为 true

示例

  1. 当变量小于 10 时,将其打印到控制台并加 1:
let i = 1;

do {
  console.log(i);
  i++;
} while (i < 10);

// Output:
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9

2. 如果 conditiontrue,推送到一个数组:

const myArray = [];
let i = 10;

do {
  myArray.push(i);
  i++;
} while (i < 10);

console.log(myArray);

// Output:
// [10]