因为C/Java/Python/JS的for, while, do while循环可以说是一幕一样的。所以如果有相关基础就可以直接跳过了。所以我这里的讲授方式是面向beginner的。
但在这一章我还额外讲了一个javascripts特有for in(类似python的for key in dict)和for of(类似python的(for value in list))。for in不太常用,但是for of用的还是不算少的因此值得跳到相应位置了解下。
循环是编程中的基本控制结构,它允许我们重复执行一段代码直到满足特定条件。 就像告诉某人”向东走5步”一样,循环让计算机能够自动重复执行相同的操作。
// 简单的循环示例for (let step = 0; step < 5; step++) { console.log("向东走一步"); // 执行5次}JavaScript 提供了多种循环语句,每种都适用于不同的场景。
for 循环是最常用的循环类型,适用于已知循环次数的情况。
for (初始化; 条件; 更新) { // 执行的代码}true 则执行循环体// 计算1到10的和let sum = 0;for (let i = 1; i <= 10; i++) { sum += i; console.log(`当前i=${i}, 累计sum=${sum}`);}console.log(`最终结果: ${sum}`); // 55
// 遍历数组const fruits = ["apple", "banana", "orange"];for (let i = 0; i < fruits.length; i++) { console.log(`第${i + 1}个水果是: ${fruits[i]}`);}// 打印乘法表for (let i = 1; i <= 9; i++) { let row = ""; for (let j = 1; j <= i; j++) { row += `${j}×${i}=${i * j} `; } console.log(row);}while 循环在条件为真时持续执行,适用于不确定循环次数的情况。
while (条件) { // 执行的代码}// 生成随机数直到得到指定值let randomNum;let attempts = 0;while (randomNum !== 7) { randomNum = Math.floor(Math.random() * 10) + 1; // 1-10的随机数 attempts++; console.log(`第${attempts}次尝试,得到数字: ${randomNum}`);}console.log(`总共尝试了${attempts}次`);
// 用户输入验证(模拟场景)let userInput = "";const validInputs = ["yes", "no"];while (!validInputs.includes(userInput.toLowerCase())) { // 在实际应用中,这里会是真实的用户输入 userInput = Math.random() > 0.7 ? "yes" : "maybe"; // 模拟输入 console.log(`用户输入: ${userInput}`); if (!validInputs.includes(userInput.toLowerCase())) { console.log("请输入 'yes' 或 'no'"); }}do...while 循环至少执行一次循环体,然后检查条件。
do { // 执行的代码} while (条件);// 至少询问用户一次let userWantsToContinue;do { console.log("执行一些操作..."); // 模拟用户选择 userWantsToContinue = Math.random() > 0.6; console.log(`用户是否想继续: ${userWantsToContinue}`);} while (userWantsToContinue);
// 菜单系统let choice;do { console.log("=== 主菜单 ==="); console.log("1. 选项1"); console.log("2. 选项2"); console.log("0. 退出");
// 模拟用户选择 choice = Math.floor(Math.random() * 3); // 0, 1, 或 2 console.log(`用户选择: ${choice}`);
switch (choice) { case 1: console.log("执行选项1"); break; case 2: console.log("执行选项2"); break; case 0: console.log("再见!"); break; }} while (choice !== 0);for...in 循环用于遍历对象的可枚举属性。
for (let key in object) { // 执行的代码}const person = { name: "Kayn", age: 17, city: "Shanghai", hobby: "coding",};
// 遍历对象属性for (let key in person) { console.log(`${key}: ${person[key]}`);}
// 统计对象属性数量let propertyCount = 0;for (let key in person) { propertyCount++;}console.log(`对象共有 ${propertyCount} 个属性`);
// 注意:for...in 也能遍历数组,但不推荐const colors = ["red", "green", "blue"];for (let index in colors) { console.log(`索引${index}: ${colors[index]}`); // index 是字符串类型}Object.keys(obj).forEach(...)Object.entries(obj).forEach(([k,v]) => ...)来代替。
👉 所以 for..in 出现频率 越来越低。
for...of 循环用于遍历可迭代对象(数组、字符串、Map、Set等)的值。
for (let value of iterable) { // 执行的代码}// 遍历数组const numbers = [10, 20, 30, 40, 50];for (let num of numbers) { console.log(`当前数字: ${num}`);}
// 遍历字符串const message = "Hello";for (let char of message) { console.log(`字符: ${char}`);}
// 遍历 Setconst uniqueNumbers = new Set([1, 2, 3, 2, 1]);for (let num of uniqueNumbers) { console.log(`唯一数字: ${num}`); // 1, 2, 3}
// 遍历 Mapconst userMap = new Map([ ["name", "Kayn"], ["age", 17], ["role", "student"],]);
for (let [key, value] of userMap) { console.log(`${key} -> ${value}`);}
// 同时获取数组的索引和值const fruits = ["apple", "banana", "orange"];for (let [index, fruit] of fruits.entries()) { console.log(`${index}: ${fruit}`);}break 用于立即跳出循环
// 找到第一个偶数就停止const numbers = [1, 3, 5, 8, 9, 12];for (let num of numbers) { if (num % 2 === 0) { console.log(`找到第一个偶数: ${num}`); break; // 跳出循环 } console.log(`检查数字: ${num}`);}
// 在嵌套循环中使用 breakouter: for (let i = 1; i <= 3; i++) { for (let j = 1; j <= 3; j++) { if (i === 2 && j === 2) { console.log("跳出外层循环"); break outer; // 跳出外层循环 } console.log(`i=${i}, j=${j}`); }}continue 用于跳过当前迭代,继续下一次迭代
// 跳过偶数,只打印奇数for (let i = 1; i <= 10; i++) { if (i % 2 === 0) { continue; // 跳过偶数 } console.log(`奇数: ${i}`);}
// 处理数组中的有效数据const data = [1, null, 3, undefined, 5, "", 7];for (let item of data) { if (!item) { continue; // 跳过无效数据 } console.log(`处理数据: ${item}`);}| 循环类型 | 适用场景 | 优点 | 注意事项 |
|---|---|---|---|
| for | 已知循环次数 | 简洁、性能好 | 适合数组遍历 |
| while | 条件循环,次数不确定 | 灵活 | 小心死循环 |
| do…while | 至少执行一次 | 保证执行 | 使用较少 |
| for…in | 遍历对象属性 | 简单直接 | 会遍历原型链 |
| for…of | 遍历可迭代对象的值 | 现代、清晰 | 不能直接获取索引 |
const largeArray = Array.from({ length: 1000000 }, (_, i) => i);
// 性能测试函数function timeTest(name, fn) { const start = performance.now(); fn(); const end = performance.now(); console.log(`${name}: ${end - start}ms`);}
// 传统 for 循环 - 通常最快timeTest("传统 for 循环", () => { let sum = 0; for (let i = 0; i < largeArray.length; i++) { sum += largeArray[i]; }});
// for...of 循环 - 稍慢但更易读timeTest("for...of 循环", () => { let sum = 0; for (let num of largeArray) { sum += num; }});
// forEach 方法 - 最慢但功能强大timeTest("forEach 方法", () => { let sum = 0; largeArray.forEach((num) => (sum += num));});// 购物车总价计算const cart = [ { name: "笔记本", price: 1200, quantity: 1 }, { name: "鼠标", price: 80, quantity: 2 }, { name: "键盘", price: 200, quantity: 1 },];
let total = 0;for (let item of cart) { const itemTotal = item.price * item.quantity; console.log(`${item.name}: ${itemTotal}元`); total += itemTotal;}console.log(`购物车总计: ${total}元`);// 批量处理页面元素(浏览器环境)const buttons = document.querySelectorAll(".btn");for (let button of buttons) { button.addEventListener("click", function () { console.log(`按钮被点击: ${this.textContent}`); });}
// 动态创建列表项const todoList = ["学习JavaScript", "完成作业", "锻炼身体"];const ul = document.createElement("ul");
for (let [index, todo] of todoList.entries()) { const li = document.createElement("li"); li.textContent = `${index + 1}. ${todo}`; ul.appendChild(li);}// 冒泡排序function bubbleSort(arr) { const result = [...arr]; // 复制数组
for (let i = 0; i < result.length - 1; i++) { let swapped = false;
for (let j = 0; j < result.length - 1 - i; j++) { if (result[j] > result[j + 1]) { // 交换元素 [result[j], result[j + 1]] = [result[j + 1], result[j]]; swapped = true; } }
if (!swapped) break; // 优化:如果没有交换说明已排序 }
return result;}
const numbers = [64, 34, 25, 12, 22, 11, 90];console.log("排序前:", numbers);console.log("排序后:", bubbleSort(numbers));// ❌ 死循环示例// let i = 0;// while (i < 10) {// console.log(i);// // 忘记更新 i,导致死循环// }
// ✅ 正确做法let i = 0;while (i < 10) { console.log(i); i++; // 记得更新计数器}const arr = [1, 2, 3];arr.customProperty = "自定义属性";
// ❌ for...in 会遍历自定义属性console.log("使用 for...in:");for (let key in arr) { console.log(key, arr[key]); // 会包括 customProperty}
// ✅ for...of 只遍历数组元素console.log("使用 for...of:");for (let value of arr) { console.log(value); // 只有 1, 2, 3}// ❌ 问题代码console.log("错误的异步循环:");for (let i = 0; i < 3; i++) { setTimeout(() => { console.log(i); // 输出三个 3 }, 100);}
// ✅ 解决方案1:使用 let 声明console.log("使用 let 解决:");for (let i = 0; i < 3; i++) { setTimeout(() => { console.log(i); // 输出 0, 1, 2 }, 200);}
// ✅ 解决方案2:使用闭包console.log("使用闭包解决:");for (var i = 0; i < 3; i++) { ((index) => { setTimeout(() => { console.log(index); // 输出 0, 1, 2 }, 300); })(i);}const largeArray = Array.from({ length: 10000 }, (_, i) => i);
// ❌ 每次都计算 lengthfor (let i = 0; i < largeArray.length; i++) { // 处理数据}
// ✅ 缓存 length 值const len = largeArray.length;for (let i = 0; i < len; i++) { // 处理数据}
// ✅ 或者使用 for...of(更简洁)for (let item of largeArray) { // 处理数据}const numbers = [1, 2, 3, 4, 5];
// 传统 for 循环let doubled1 = [];for (let i = 0; i < numbers.length; i++) { doubled1.push(numbers[i] * 2);}
// 现代方法:maplet doubled2 = numbers.map((n) => n * 2);
// 传统循环查找let found1 = null;for (let num of numbers) { if (num > 3) { found1 = num; break; }}
// 现代方法:findlet found2 = numbers.find((n) => n > 3);
console.log("传统循环结果:", doubled1, found1);console.log("现代方法结果:", doubled2, found2);// 模拟异步操作function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms));}
async function processItems() { const items = [1, 2, 3, 4, 5];
// 顺序处理 console.log("顺序处理:"); for (let item of items) { await delay(100); console.log(`处理项目: ${item}`); }
// 并行处理 console.log("并行处理:"); const promises = items.map(async (item) => { await delay(100); return `处理项目: ${item}`; });
const results = await Promise.all(promises); results.forEach((result) => console.log(result));}
// processItems(); // 取消注释以运行循环是编程的基础工具,选择合适的循环类型能让代码更高效、更易读:
记住要避免死循环、注意异步操作的陷阱,并且在现代 JavaScript 开发中考虑使用数组方法来替代传统循环,以获得更好的可读性和函数式编程的优势。