JavaScript 循环控制:break 和 continue
在上一章中,我们学习了 for、while 和 do...while 循环,它们允许我们多次执行一段代码。
然而,有时我们需要对循环的行为进行更精细的控制。如果你正在寻找某样东西并且提前找到了,不再需要检查剩余的项目怎么办?或者,如果某个条件使得循环的某次特定迭代变得无关紧要,但你仍然希望循环继续执行后续的迭代怎么办?
JavaScript 提供了两个特殊的语句——break 和 continue——赋予了我们这种精确的控制力,允许我们修改循环的正常执行流程。
1. break 语句:提前停止循环
break 语句用于立即终止最近的包围它的 for、while、do...while 或 switch 语句。
当在循环内部遇到 break 时,循环完全停止,程序执行将继续从循环后的下一条语句开始。你可以把它想象成循环的紧急出口。
1.1 为什么以及何时使用 break
使用 break 的主要原因是在循环内部满足了特定条件,使得进一步的迭代变得不必要或适得其反。这可以通过防止无用的计算来显著提高代码的效率。
使用 break 的常见场景:
- 搜索项目: 如果你正在遍历一个列表(比如数组)来查找特定值,一旦找到该值,就没有必要继续搜索了。
break允许你立即退出循环。 - 处理特定条件: 如果发生错误,或达到了目标阈值,
break可以停止原本会继续的循环。 - 退出“无限”循环: 有时,我们会故意使用
while(true)循环,而break提供了基于循环体内部条件(如用户输入或处理的数据计数)退出该循环的机制。
1.2 break 的详细示例
让我们看一些实际例子来了解 break 的作用。
1.2.1 示例 1:查找第一次出现
想象你有一列数字,想看看数字 7 是否存在。一旦找到它,就不需要检查剩余的数字了。
const numbers = [1, 5, 3, 9, 7, 2, 8, 4];
let foundSeven = false;
// 遍历数组中的每个数字
for (let i = 0; i < numbers.length; i++) {
const currentNumber = numbers[i];
console.log(`正在检查数字: ${currentNumber}`);
// 检查当前数字是否为 7
if (currentNumber === 7) {
console.log("找到了数字 7!");
foundSeven = true;
break; // 立即退出循环
}
}
if (foundSeven) {
console.log("搜索结束: 找到了 7。");
} else {
console.log("搜索结束: 没有找到 7。");
}
// 预期输出:
// 正在检查数字: 1
// 正在检查数字: 5
// 正在检查数字: 3
// 正在检查数字: 9
// 正在检查数字: 7
// 找到了数字 7!
// 搜索结束: 找到了 7。在这个例子中,一旦 currentNumber 变成 7,if 条件为真,foundSeven 被设置为 true,然后执行 break。循环立即终止,"正在检查数字: 2" 等后续内容永远不会被打印。这演示了 break 如何防止不必要的迭代。
1.2.2 示例 2:带有限制的用户输入验证
假设我们要让用户输入一个正数,但我们只给他们最多 3 次尝试机会。
let attempts = 0;
const maxAttempts = 3;
let userInput = 0; // 初始化为一个无效值
while (attempts < maxAttempts) {
// 在真实的浏览器中,你会使用 prompt()
// 为了演示,我们将模拟用户输入
// 尝试 1: -5 (无效)
// 尝试 2: 0 (无效)
// 尝试 3: 10 (有效)
if (attempts === 0) {
userInput = -5; // 模拟第一次输入
} else if (attempts === 1) {
userInput = 0; // 模拟第二次输入
} else {
userInput = 10; // 模拟第三次输入
}
console.log(`尝试第 ${attempts + 1} 次: 用户输入了 ${userInput}`);
if (userInput > 0) {
console.log(`收到有效输入: ${userInput}`);
break; // 因为给出了有效输入,退出循环
} else {
console.log("无效输入。请输入一个正数。");
}
attempts++;
}
if (userInput > 0) {
console.log("程序继续使用有效输入。");
} else {
console.log("达到最大尝试次数。无法获取有效输入。");
}
// 预期输出 (模拟输入):
// 尝试第 1 次: 用户输入了 -5
// 无效输入。请输入一个正数。
// 尝试第 2 次: 用户输入了 0
// 无效输入。请输入一个正数。
// 尝试第 3 次: 用户输入了 10
// 收到有效输入: 10
// 程序继续使用有效输入。在这里,即使 maxAttempts 是 3,如果在第一次或第二次尝试时输入了有效的正数,break 语句也会提前停止 while 循环,防止用户用完所有尝试次数。
1.2.3 示例 3:假设场景 - 机器人清洁任务
想象一个简单的机器人在打扫房间。它需要在电池耗尽或轮班结束前捡起 5 个特定的“灰尘团”。如果它找到了所有 5 个,它应该立即停止。
let dustBunniesCollected = 0;
const targetBunnies = 5;
const totalSpotsInRoom = 10; // 机器人将检查的地点数量
for (let spot = 1; spot <= totalSpotsInRoom; spot++) {
console.log(`机器人正在检查地点 ${spot}...`);
// 模拟在某些地点发现灰尘团
if (spot === 2 || spot === 4 || spot === 6 || spot === 7 || spot === 9) {
dustBunniesCollected++;
console.log(` 发现一个灰尘团!总共收集了: ${dustBunniesCollected}`);
}
// 检查机器人是否收集了足够的灰尘团
if (dustBunniesCollected === targetBunnies) {
console.log("机器人已收集所有目标灰尘团!");
break; // 任务完成,退出循环
}
}
if (dustBunniesCollected === targetBunnies) {
console.log("任务完成!机器人返回基地。");
} else {
console.log(`任务未完成。机器人收集了 ${targetBunnies} 个中的 ${dustBunniesCollected} 个。`);
}
// 预期输出:
// ... (前略)
// 机器人正在检查地点 9...
// 发现一个灰尘团!总共收集了: 5
// 机器人已收集所有目标灰尘团!
// 任务完成!机器人返回基地。在这个场景中,机器人不需要检查地点 10,即使它还有电量和时间,因为它的主要目标(收集 5 个灰尘团)已经达成。break 语句确保它一旦达到目标就停止搜索。
2. continue 语句:跳过一次迭代
虽然 break 完全停止循环,但 continue 语句采取了不同的方式。
当在循环内部遇到 continue 时,它会跳过循环当前迭代的剩余部分,并立即跳转到下一次迭代。循环不会停止;它只是移动到下一个周期。
2.1 为什么以及何时使用 continue
当你想针对特定条件绕过循环代码的某些部分,而不停止循环处理后续迭代时,continue 语句非常有用。
2.1.1 使用 continue 的常见场景:
- 过滤数据: 你可能正在处理一个项目列表,但有些项目无效或不符合某些标准。continue 允许你跳过处理这些特定项目,移动到下一个有效项目。
- 跳过特定情况: 如果循环内的某个计算或操作仅针对特定类型的数据,continue 可以有效地绕过不需要类型的代码。
- 优化处理: 当你知道某个特定的迭代不需要进一步操作时,continue 可以防止执行该迭代中不必要的代码。
2.2 continue 的详细示例
让我们探索如何使用 continue 来跳过循环的部分内容。
2.2.1 示例 1:只打印奇数
假设你想打印从 1 到 10 的所有数字,但你只对奇数感兴趣。
console.log("打印 1 到 10 中的奇数:");
for (let i = 1; i <= 10; i++) {
// 检查当前数字是否为偶数
if (i % 2 === 0) {
// 如果是偶数,跳过本次迭代的剩余部分,进入下一个数字
console.log(` 跳过偶数: ${i}`);
continue;
}
// 这段代码只有在数字是奇数时才会运行
console.log(`奇数: ${i}`);
}
// 预期输出:
// 打印 1 到 10 中的奇数:
// 奇数: 1
// 跳过偶数: 2
// 奇数: 3
// ...这里,当 i 是偶数时,if (i % 2 === 0) 条件为真,执行 continue。这会立即跳转到下一次迭代(即 i++),跳过该偶数的 console.log("奇数: ${i}") 语句。
2.2.2 示例 2:处理有效数据条目
想象你在处理一列用户评论。有些评论可能是空的,或者包含不当词汇。你想跳过这些,只处理有效的评论。
const comments = [
"文章写得好!",
"", // 空评论
"这很有用。",
"spam spam spam", // 不当内容 (模拟)
"谢谢分享。"
];
const inappropriateWords = ["spam", "badword", "offensive"];
console.log("正在处理评论:");
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
// 检查空评论
if (comment.trim() === "") { // .trim() 移除首尾空格
console.log(` 跳过索引 ${i} 的空评论`);
continue; // 跳到下一条评论
}
// 检查不当词汇
let hasInappropriateContent = false;
for (const word of inappropriateWords) {
if (comment.toLowerCase().includes(word)) {
hasInappropriateContent = true;
break; // 发现不当词汇,无需为*这条*评论检查其他词汇
}
}
if (hasInappropriateContent) {
console.log(` 跳过索引 ${i} 的不当评论: "${comment}"`);
continue; // 跳到下一条评论
}
// 如果运行到这里,说明评论是有效的,可以处理
console.log(`正在处理有效评论: "${comment}"`);
}
// 预期输出:
// 正在处理评论:
// 正在处理有效评论: "文章写得好!"
// 跳过索引 1 的空评论
// 正在处理有效评论: "这很有用。"
// 跳过索引 3 的不当评论: "spam spam spam"
// 正在处理有效评论: "谢谢分享。"在这个场景中,continue 帮助我们有效地过滤掉了不应处理的评论。循环仍然检查每条评论,但对于特定的评论,它会跳过“处理有效评论”的步骤。
2.2.3 示例 3:假设场景 - 库存检查
商店经理正在检查库存。他们只想统计“有货”且价格大于 0 的商品。“缺货”或价格无效的商品应被跳过。
const inventoryItems = [
{ name: "笔记本电脑", status: "有货", price: 1200 },
{ name: "鼠标", status: "有货", price: 25 },
{ name: "键盘", status: "缺货", price: 75 },
{ name: "显示器", status: "有货", price: 300 },
{ name: "摄像头", status: "有货", price: -10 }, // 无效价格
{ name: "耳机", status: "缺货", price: 50 }
];
let totalValidItemsCount = 0;
let totalValidItemsValue = 0;
console.log("正在进行库存检查:");
for (let i = 0; i < inventoryItems.length; i++) {
const item = inventoryItems[i];
// 跳过“缺货”的商品
if (item.status === "缺货") {
console.log(` 跳过 "${item.name}" - 缺货。`);
continue;
}
// 跳过价格无效(非正数)的商品
if (item.price <= 0) {
console.log(` 跳过 "${item.name}" - 价格无效。`);
continue;
}
// 如果运行到这里,说明商品是有效的
console.log(` 处理有效商品: "${item.name}" (价格: $${item.price})`);
totalValidItemsCount++;
totalValidItemsValue += item.price;
}
console.log("\n库存检查完成!");
console.log(`找到有效商品总数: ${totalValidItemsCount}`);
console.log(`有效商品总价值: $${totalValidItemsValue}`);这个例子展示了如何在单个循环中多次使用 continue 来基于不同标准过滤项目,确保只处理相关数据进行计算。
3. 实践示例与演示
让我们在一个稍微复杂的场景中结合我们对 break 和 continue 的理解。
3.1 示例:模拟带有尝试次数的简单测验
想象构建一个简单的测验。用户有 3 次机会回答一个问题。如果他们答对了,我们继续;否则,他们失去一次机会。
const correctAnswer = "paris";
let score = 0;
const maxAttemptsPerQuestion = 3;
console.log("欢迎参加地理测验!");
console.log("问题: 法国的首都是哪里?");
let attemptsRemaining = maxAttemptsPerQuestion;
let answeredCorrectly = false;
while (attemptsRemaining > 0) {
// 模拟用户输入以进行演示
let userAnswer;
if (attemptsRemaining === 3) {
userAnswer = "london"; // 错误
} else if (attemptsRemaining === 2) {
userAnswer = "berlin"; // 错误
} else {
userAnswer = "paris"; // 正确
}
console.log(`\n你的猜测 (尝试 ${maxAttemptsPerQuestion - attemptsRemaining + 1}/${maxAttemptsPerQuestion}): ${userAnswer}`);
if (userAnswer.toLowerCase() === correctAnswer) {
console.log("正确!你答对了!");
score++;
answeredCorrectly = true;
break; // 因为问题已正确回答,退出循环
} else {
attemptsRemaining--;
if (attemptsRemaining > 0) {
console.log(`不正确。你还剩 ${attemptsRemaining} 次尝试机会。`);
} else {
console.log("不正确。你的尝试机会已用完。");
}
}
}
// ... (后续逻辑)这个组合示例展示了用于在达成目标(正确答案)时停止流程的 break。