跳到主要内容

JavaScript 循环

在编程中,循环是一种基础且强大的结构,它允许我们重复执行特定的代码块,直到满足某个条件为止。JavaScript提供了多种循环方式,适用于不同的场景。掌握这些循环结构对于编写高效的JavaScript代码至关重要。

为什么需要循环?

想象一下,如果你需要打印数字1到100,没有循环的话,你需要写100行几乎相同的代码。使用循环,你只需几行代码就能完成同样的任务。循环使代码更简洁、更易于维护,同时也提高了程序的性能。

JavaScript 中的循环类型

JavaScript主要提供以下几种循环结构:

  1. for 循环
  2. while 循环
  3. do-while 循环
  4. for...in 循环
  5. for...of 循环
  6. 数组方法(如forEachmapfilter等)

让我们一一详细了解每种循环的使用方法和适用场景。

for循环

for循环是最常用的循环类型之一,尤其当你知道循环需要执行的次数时。

基本语法

javascript
for (初始化; 条件; 递增/递减) {
// 循环体,当条件为true时执行
}

示例

javascript
// 打印1到5的数字
for (let i = 1; i <= 5; i++) {
console.log(i);
}

输出:

1
2
3
4
5

for循环的组成部分

  1. 初始化:在循环开始前执行一次,通常用于初始化计数器。
  2. 条件:在每次循环迭代前检查,如果为true则继续循环,否则退出循环。
  3. 递增/递减:在每次循环体执行后执行,通常用于更新计数器。
提示

for循环的三个部分都是可选的,但分号是必须的。例如,for (;;) { ... }创建一个无限循环。

实际应用示例:遍历数组

javascript
const fruits = ['苹果', '香蕉', '橙子', '葡萄', '芒果'];

for (let i = 0; i < fruits.length; i++) {
console.log(`${i+1}个水果是:${fruits[i]}`);
}

输出:

第1个水果是:苹果
第2个水果是:香蕉
第3个水果是:橙子
第4个水果是:葡萄
第5个水果是:芒果

while循环

while循环在指定条件为真时重复执行代码块。它最适合当你不确定循环需要执行多少次时使用。

基本语法

javascript
while (条件) {
// 循环体,当条件为true时执行
}

示例

javascript
let i = 1;
while (i <= 5) {
console.log(i);
i++;
}

输出:

1
2
3
4
5

实际应用示例:猜数字游戏

javascript
const targetNumber = Math.floor(Math.random() * 10) + 1;
let guess = 0;
let attempts = 0;

// 模拟用户猜测,直到猜对为止
while (guess !== targetNumber) {
guess = Math.floor(Math.random() * 10) + 1; // 随机生成1-10的猜测数字
attempts++;
console.log(`尝试 #${attempts}: 猜测 ${guess}`);
}

console.log(`恭喜!猜对了目标数字 ${targetNumber},共用了 ${attempts} 次尝试。`);
警告

使用while循环时,务必确保循环条件最终会变为false,否则将创建无限循环,可能导致浏览器或程序崩溃。

do-while循环

do-while循环与while循环相似,但它会先执行一次循环体,然后再检查条件。这确保循环体至少执行一次。

基本语法

javascript
do {
// 循环体,至少执行一次
} while (条件);

示例

javascript
let i = 1;
do {
console.log(i);
i++;
} while (i <= 5);

输出:

1
2
3
4
5

与while循环的区别

当条件一开始就为false时,while循环不会执行循环体,而do-while循环会执行一次循环体。

javascript
let i = 6;

// while循环
while (i <= 5) {
console.log(`while: ${i}`);
i++;
}

// 重置i
i = 6;

// do-while循环
do {
console.log(`do-while: ${i}`);
i++;
} while (i <= 5);

输出:

do-while: 6

实际应用示例:用户输入验证

javascript
function simulateUserInput() {
// 模拟用户输入,随机返回一个有效或无效的值
return Math.random() > 0.5 ? "有效输入" : "";
}

let userInput;
let attempt = 0;

do {
attempt++;
userInput = simulateUserInput();
console.log(`尝试 #${attempt}: 用户输入为 "${userInput}"`);
} while (userInput === "");

console.log(`收到有效输入:${userInput},共尝试 ${attempt}`);

for...in 循环

for...in循环主要用于遍历对象的属性。

基本语法

javascript
for (let key in object) {
// 使用key访问object[key]
}

示例:遍历对象属性

javascript
const person = {
firstName: "张",
lastName: "三",
age: 30,
city: "北京"
};

for (let key in person) {
console.log(`${key}: ${person[key]}`);
}

输出:

firstName: 张
lastName: 三
age: 30
city: 北京
注意

不建议使用for...in循环遍历数组,因为它也会遍历数组的非数字属性和原型链上的属性。如果要遍历数组,应使用for循环、for...of循环或数组方法。

for...of 循环

ES6引入的for...of循环用于遍历可迭代对象(如数组、字符串、Map、Set等)。

基本语法

javascript
for (let value of iterable) {
// 使用value
}

示例:遍历数组

javascript
const fruits = ['苹果', '香蕉', '橙子'];

for (let fruit of fruits) {
console.log(fruit);
}

输出:

苹果
香蕉
橙子

示例:遍历字符串

javascript
const message = "Hello";

for (let char of message) {
console.log(char);
}

输出:

H
e
l
l
o

for...of 与 for...in 的区别

  • for...in循环遍历对象的可枚举属性(键)。
  • for...of循环遍历可迭代对象的值。
javascript
const arr = ['a', 'b', 'c'];

// 添加一个非数字属性
arr.test = "不是数组元素";

// for...in遍历键(包括非数字属性)
for (let key in arr) {
console.log(`for...in: ${key} -> ${arr[key]}`);
}

// for...of只遍历值(不包括非数字属性)
for (let value of arr) {
console.log(`for...of: ${value}`);
}

输出:

for...in: 0 -> a
for...in: 1 -> b
for...in: 2 -> c
for...in: test -> 不是数组元素
for...of: a
for...of: b
for...of: c

数组迭代方法

除了传统的循环结构外,JavaScript数组还提供了多种内置方法用于迭代。这些方法更加简洁和功能化。

forEach()

forEach()方法对数组的每个元素执行一次提供的函数。

javascript
const numbers = [1, 2, 3, 4, 5];

numbers.forEach(function(number, index) {
console.log(`索引 ${index} 的值是: ${number}`);
});

输出:

索引 0 的值是: 1
索引 1 的值是: 2
索引 2 的值是: 3
索引 3 的值是: 4
索引 4 的值是: 5

map()

map()方法创建一个新数组,其结果是该数组中的每个元素调用一次提供的函数后的返回值。

javascript
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(function(number) {
return number * 2;
});

console.log("原始数组:", numbers);
console.log("加倍后:", doubled);

输出:

原始数组: [1, 2, 3, 4, 5]
加倍后: [2, 4, 6, 8, 10]

filter()

filter()方法创建一个新数组,其包含通过所提供函数实现的测试的所有元素。

javascript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const evenNumbers = numbers.filter(function(number) {
return number % 2 === 0;
});

console.log("所有数字:", numbers);
console.log("偶数:", evenNumbers);

输出:

所有数字: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
偶数: [2, 4, 6, 8, 10]

其他数组方法

JavaScript还有其他有用的数组迭代方法,如reduce()some()every()等。这些方法各自有特定的用途,值得学习掌握。

循环控制语句

JavaScript提供了两个特殊的语句来控制循环的执行流程:breakcontinue

break

break语句用于完全终止当前循环。

javascript
for (let i = 1; i <= 10; i++) {
if (i === 5) {
console.log("遇到数字5,终止循环");
break;
}
console.log(i);
}

输出:

1
2
3
4
遇到数字5,终止循环

continue

continue语句用于跳过当前迭代,直接进入下一次迭代。

javascript
for (let i = 1; i <= 5; i++) {
if (i === 3) {
console.log("跳过数字3");
continue;
}
console.log(i);
}

输出:

1
2
跳过数字3
4
5

嵌套循环

可以在一个循环内部包含另一个循环,这称为嵌套循环。外层循环的每次迭代都会完整执行一次内层循环。

javascript
// 打印乘法表(1-5)
for (let i = 1; i <= 5; i++) {
let row = "";
for (let j = 1; j <= 5; j++) {
row += `${i}x${j}=${i*j}\t`;
}
console.log(row);
}

输出:

1x1=1   1x2=2   1x3=3   1x4=4   1x5=5
2x1=2 2x2=4 2x3=6 2x4=8 2x5=10
3x1=3 3x2=6 3x3=9 3x4=12 3x5=15
4x1=4 4x2=8 4x3=12 4x4=16 4x5=20
5x1=5 5x2=10 5x3=15 5x4=20 5x5=25
备注

嵌套循环的时间复杂度是外层循环次数乘以内层循环次数,例如上面的例子复杂度为O(n²),当数据量大时要谨慎使用。

循环的性能考虑

不同的循环在性能上有细微差别,但对于大多数应用来说,选择循环类型更应基于代码清晰度和适用场景,而不是微小的性能差异。

一般来说:

  1. 传统的for循环通常是最快的,特别是当你需要控制步长时。
  2. 数组方法(如forEachmap等)虽然可能稍慢,但代码更简洁、更容易理解。
  3. 避免在循环中修改循环变量(除了循环控制语句)。
  4. 当处理大数据集时,考虑使用for循环而非forEach以获得更好的性能。

实际应用案例:购物车总价计算

下面是一个使用循环处理购物车数据的实例,展示了几种不同循环方式的应用:

javascript
const cart = [
{ name: "T恤", price: 99, quantity: 2 },
{ name: "牛仔裤", price: 199, quantity: 1 },
{ name: "运动鞋", price: 299, quantity: 1 },
{ name: "帽子", price: 59, quantity: 3 }
];

// 使用for循环计算总价
let totalWithFor = 0;
for (let i = 0; i < cart.length; i++) {
totalWithFor += cart[i].price * cart[i].quantity;
}
console.log(`使用for循环计算的总价: ¥${totalWithFor}`);

// 使用for...of循环计算总价
let totalWithForOf = 0;
for (let item of cart) {
totalWithForOf += item.price * item.quantity;
}
console.log(`使用for...of循环计算的总价: ¥${totalWithForOf}`);

// 使用forEach计算总价
let totalWithForEach = 0;
cart.forEach(item => {
totalWithForEach += item.price * item.quantity;
});
console.log(`使用forEach计算的总价: ¥${totalWithForEach}`);

// 使用reduce计算总价
const totalWithReduce = cart.reduce((sum, item) => {
return sum + (item.price * item.quantity);
}, 0);
console.log(`使用reduce计算的总价: ¥${totalWithReduce}`);

输出:

使用for循环计算的总价: ¥873
使用for...of循环计算的总价: ¥873
使用forEach计算的总价: ¥873
使用reduce计算的总价: ¥873

总结

循环是JavaScript编程的基础构建块之一,掌握不同类型的循环及其适用场景对于编写高效的代码至关重要:

  • for循环:当你知道循环需要执行的精确次数时使用。
  • while循环:当你不知道循环需要执行多少次但知道循环条件时使用。
  • do-while循环:当你需要循环体至少执行一次时使用。
  • for...in循环:用于遍历对象的属性。
  • for...of循环:用于遍历数组或其他可迭代对象的值。
  • 数组方法:如forEachmapfilter等,提供了更简洁、更功能化的迭代方式。

选择合适的循环类型可以使代码更简洁、更易于理解和维护。

练习题

为了巩固对循环的理解,尝试完成以下练习:

  1. 使用for循环打印出1到100之间的所有偶数。
  2. 使用while循环计算1到10的所有整数之和。
  3. 使用for...of循环找出数组[12, 5, 8, 130, 44]中的最大值。
  4. 使用filtermap方法筛选出数组[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]中的偶数并将它们加倍。
  5. 编写一个函数,接收一个正整数n,并使用循环打印出n阶乘的结果。

进阶资源

通过持续练习和应用,你将能够熟练运用JavaScript中的各种循环结构,编写更高效、更简洁的代码。