JavaScript 循环

JavaScript 支持通过 forwhile 关键字重复执行代码块。类似的行为也可以通过数据类型 Array(以及类似的数据类型)的 forEach 方法实现。

for 循环

for 语句的语法为:

for (<初始表达式>; <条件表达式>; <最终表达式>) { <代码块> }

它的执行流程遵循以下规则:

  1. 初始表达式 执行一次,通常用于声明和初始化一个或多个变量,通常是整数类型的变量。有时,变量的声明会在 for 语句之前进行。
  2. 条件表达式 被评估。如果返回 true,执行第 3 步。如果返回 false,循环终止。
  3. 代码块 执行。
  4. 最终表达式 执行,通常用于增加或减少与条件相关的变量。
  5. 循环重复,从第 2 步开始。
for (let i = 0; i < 10; i++) {
  // 代码块
}

示例:展示从 0 到 10 的偶数平方,并显示这些数字的和。

"use strict";

const upperLimit = 10;
let sum = 0;
let tmpString = "";

for (let i = 0; i <= upperLimit; i++) {
  sum = sum + i;
  if (i % 2 === 0) {
    tmpString += i * i + "; "
  }
}
alert("The sum is: " + sum + ". The square numbers are: " + tmpString);

可选的语法部分

  • 初始表达式、条件表达式和最终表达式是可选的。如果省略其中一个或多个,它们将被其他语句替代,以控制循环。例如:
"use strict";

// 无限循环,除非使用 'break' 来终止(见下文)
for (;;) {
  // ...
  break;
}

const answer = prompt("With which start value shall the loop begin?");
let i = Number(answer);
for (; i >= 0 && i < 10; i++) {
  // 在循环外计算的起始值
}

for (let i = 0; i < 10;) {
  // ...
  if (true) { // 用任意其他条件控制递增
    i++;
  }
}

嵌套循环

for 循环可以嵌套使用。你可以在第一个(外部)循环的代码块中使用第二个(内部)循环。

"use strict";

const maxInner = 10;
const maxOuter = 4;
let myString = "";

for (let o = 1; o <= maxOuter; o++) {
  myString = "";
  // 小心。很容易混淆内外循环的变量。
  for (let i = 1; i <= maxInner; i++) {
    myString = myString + o * i + ", ";
  }
  alert(myString);
}

这种循环嵌套结构在其他循环构造中也是可以实现的。

continue / break

有时我们只希望部分代码或某个代码块执行。这可以通过一个或多个 if / else 语句实现。如果合适的话,可以通过关键字 continue 来简化这些条件语句。当 continue 被执行时,跳过代码块中剩余的部分,直接执行步骤 4 中的最终表达式。

示例:使用 continue

"use strict";

for (let i = 0; i <= 6; i++) {  
  if (i === 5) { 
    continue;   // 跳过代码块的剩余部分
  }
  alert(i);     // 0, 1, 2, 3, 4, 6
}

在这个简单的示例中,当 i 等于 5 时,跳过代码块的其余部分。当然,表达方式可以有所不同。更实际的例子可能是在数据库查询结果的循环中,跳过对某些特定状态的复杂处理。

break 关键字

break 关键字与 continue 类似,但更为严格。它不仅跳过代码块的其余部分,还会终止整个循环。

"use strict";

for (let i = 0; i <= 6; i++) {  
  if (i === 5) { 
    break;      // 终止整个循环
  }
  alert(i);     // 0, 1, 2, 3, 4
}

实际场景中,比如数据库查询结果的循环中,如果连接在循环中断开,使用 break 终止循环。

你可以在所有讨论过的循环形式中使用 continuebreak

do {} while ()

语句的语法是:

do { <代码块> } while (<条件>);

它根据以下规则执行:

  1. 代码块执行。由于这是第一步,代码块至少会执行一次。如果你想确保某些操作在任何情况下都能执行时,这非常有用。
  2. 条件评估。如果条件返回 true,则重复执行步骤 1。如果返回 false,循环终止。请注意,在此结构中没有专门的部分用于操作检查条件的变量。你必须在代码块内的其他语句中处理。
"use strict";

let counter = 100;
do {
  counter++;
  alert(counter);  // ... 或其他日志记录
} while (counter < 10);

while () {}

while (<条件>) { <代码块> } 语法与 do { <代码块> } while (<条件>) 非常相似。唯一的区别是 while 会在执行代码块之前检查条件,而 do 会在执行代码块之后检查条件。

"use strict";

let counter = 0;
while (counter < 10) {
  counter++;
  alert(counter);  // 每次循环都会输出当前的 counter 值
}

for (x in Object) {}

这种语言构造用于遍历对象的属性。其语法为:

for (<变量> in <对象>) { <代码块> }

变量会依次接收对象中所有属性的键。对于每个键,代码块会执行一次。

"use strict";

const myObj = {firstName: "Marilyn", familyName: "Monroe", born: 1953};

for (const key in myObj) {
  alert(key); // firstName, familyName, born
  // alert(myObj[key]);  // 如果想查看属性值
}

数组是特殊的对象,因此也可以在数组上使用 for..in。对于数组,键是从 0 开始的整数,这正是 for..in 循环所提取的内容。

"use strict";

const myArray = ["certo", "uno", "dos", "tres"];
for (const key in myArray) {
  alert(key); // 0, 1, 2, 3
  // alert(myArray[key]);  // 如果想查看值
}

数组也接受非数字键,例如:myArray["four"] = "cuatro";。与 for..of 循环不同,for..in 循环会处理这种非数字键。

for (x of Array) {}

这种语言构造用于遍历数组。其语法为:

for (<变量> of <可迭代对象>) { <代码块> }

变量会依次接收数组中的所有值。对于每个值,代码块会执行一次。

这看起来与上面的 for..in 循环相似,但有显著的区别:

  • 它返回数组的值,而不是键或索引。
  • 它只对所有可迭代对象(如 Array、Map、Set 等)有效,而 object 数据类型不可迭代。
  • 它只处理数值型的键,非数值型的键会被静默忽略。
"use strict";

const myArray = ["cero", "uno", "dos", "tres"];
myArray["four"] = "cuatro";

for (const myValue of myArray) {
  alert(myValue); // cero, uno, dos, tres。没有输出 'cuatro',因为键是字符串。
}

总结:

  • for..in 用于遍历对象的键,可以用于数组,但会处理所有键(包括非数字键)。
  • for..of 用于遍历数组的值,只处理数值型的键,忽略非数字键。

for..in vs. for..of

for..infor..of 之间的区别可以通过以下示例来总结:

"use strict";

const myObj = {firstName: "Marilyn", familyName: "Monroe", born: 1953};
const myArray = ["cero", "uno", "dos", "tres"];
myArray["four"] = "cuatro";

// for..of 不允许用于对象;只能用于 for..in
for (const x of myObj) {};  // 错误

for (const x in myArray) {
  alert(x); // 0, 1, 2, 3, four
}

for (const x of myArray) {
  alert(x); // cero, uno, dos, tres。没有 'cuatro'!
}
  • for..in:用于遍历对象的属性或数组的索引(键)。对于对象,它遍历键;对于数组,它遍历索引。
  • for..of:用于遍历数组中的值,而不是键。对于数组的非数字键(如 "four"),for..of 会忽略它们。

Object.entries() 方法

如果你想要一个遍历对象的键和值的循环,使用 Object.entries() 方法比 for..inmyObj[key] 的组合更简单。Object.entries() 返回一个包含键和值的二维数组。

"use strict";

const myObj = {firstName: "Marilyn", familyName: "Monroe", born: 1953};
const myArray = ["cero", "uno", "dos", "tres"];
myArray["four"] = "cuatro";

for (const [key, val] of Object.entries(myObj)) {
  alert(key + ' / ' + val);
}
for (const [key, val] of Object.entries(myArray)) {
  alert(key + ' / ' + val); // 包含 'four / cuatro'
}

Object.entries()Array.forEach() 方法并不是核心语言元素,它们是 ObjectArray 数据类型的方法。

Array.forEach() 方法

Array 数据类型提供了 forEach() 方法,它用于遍历数组的元素,逐个返回每个元素。这个方法的特别之处在于,它接收一个函数作为参数,这使得它与其他 forwhile 循环的区别在于它是基于回调函数的。

forEach() 方法调用的语法很简单:myArray.forEach(myFunction)。可能令人困惑的是,函数调用可以有多种缩写形式。

示例:显式调用函数

"use strict";

// 函数定义(这里并未调用函数!)
function myFunction(element) {
  alert("数组的元素是: " + element)
};

// 测试数组
const myArray = ['a', 'b', 'c'];

// 遍历数组,每次调用一次函数
myArray.forEach(myFunction);

函数调用的缩写形式

"use strict";

// 使用箭头函数语法
const myFunction = 
  (element) => {
    alert("数组的元素是: " + element)
  };

// 不换行的写法
// const myFunction = (element) => {alert("数组的元素是: " + element)};

const myArray = ['a', 'b', 'c'];
myArray.forEach(myFunction);
"use strict";
const myArray = ['a', 'b', 'c'];

// 直接将函数定义为 forEach() 的参数,这种函数称为“匿名函数”。
myArray.forEach((element) => {alert("数组的元素是: " + element)});
"use strict";
const myArray = ['a', 'b', 'c'];

// 在简单情况下,可以省略更多的语法元素
myArray.forEach(element => alert("数组的元素是: " + element));

具体使用哪种语法取决于你的偏好。

在许多情况下,调用的函数会执行副作用,例如日志记录。为了对所有数组元素进行计算,需要使用闭包技术。以下示例中,变量 sum 并未在外部上下文中定义,而是在匿名函数内定义的。

"use strict";

const myArray = [3, 0, -1, 2];
let sum = 0;

myArray.forEach(element => sum = sum + element);
alert(sum);

总结 forEach() 方法的规则:

  • 该方法可以用于可迭代对象,如 ArrayMapSetObject 数据类型不可迭代。
  • 它仅会遍历具有数字键的元素,这通常是数组的情况。
Last modified: Monday, 13 January 2025, 3:04 PM