JavaScript 循环语句
概述
循环语句用于重复执行代码块,直到满足特定条件。JavaScript提供了多种循环结构,适用于不同的使用场景。
for 循环
基本语法
for (初始化; 条件; 更新表达式) {
// 循环体
}
基础用法
// 经典的计数循环
for (let i = 0; i < 5; i++) {
console.log(`计数: ${i}`);
}
// 倒序循环
for (let i = 10; i > 0; i--) {
console.log(`倒计时: ${i}`);
}
// 步长为2的循环
for (let i = 0; i < 10; i += 2) {
console.log(`偶数位置: ${i}`);
}
数组遍历
const fruits = ['apple', 'banana', 'orange'];
// 传统方式
for (let i = 0; i < fruits.length; i++) {
console.log(`索引${i}: ${fruits[i]}`);
}
// 性能优化版本(缓存长度)
for (let i = 0, len = fruits.length; i < len; i++) {
console.log(`索引${i}: ${fruits[i]}`);
}
特殊用法
// 无限循环(需要break跳出)
for (;;) {
if (someCondition) break;
// 执行某些操作
}
// 多变量初始化
for (let i = 0, j = 10; i < j; i++, j--) {
console.log(`i=${i}, j=${j}`);
}
while 循环
基本语法
while (条件) {
// 循环体
}
使用场景
// 条件未知的循环
let count = 0;
while (count < 5) {
console.log(`计数: ${count}`);
count++;
}
// 用户输入验证
let userInput;
while (userInput !== 'quit') {
userInput = prompt('输入命令(输入quit退出):');
if (userInput !== 'quit') {
console.log(`你输入了: ${userInput}`);
}
}
// 数组处理
const numbers = [1, 2, 3, 4, 5];
let index = 0;
while (index < numbers.length) {
if (numbers[index] % 2 === 0) {
console.log(`偶数: ${numbers[index]}`);
}
index++;
}
防止无限循环
// 危险:忘记更新条件变量
let i = 0;
while (i < 10) {
console.log(i);
// 忘记了 i++ ,会造成无限循环!
}
// 安全:确保条件会改变
let i = 0;
while (i < 10) {
console.log(i);
i++; // 确保循环会结束
}
// 使用计数器防护
let attempts = 0;
const maxAttempts = 100;
while (someCondition && attempts < maxAttempts) {
// 执行操作
attempts++;
}
do-while 循环
基本语法
do {
// 循环体
} while (条件);
使用场景
// 至少执行一次的场景
let userChoice;
do {
userChoice = prompt('请选择操作(1-3):');
console.log(`你选择了: ${userChoice}`);
} while (userChoice !== '3');
// 游戏循环
let playAgain;
do {
playGame();
playAgain = confirm('再玩一次吗?');
} while (playAgain);
// 数据验证
let password;
do {
password = prompt('请输入密码(至少6位):');
if (password.length < 6) {
alert('密码太短,请重新输入!');
}
} while (password.length < 6);
for…in 循环
基本语法
for (变量 in 对象) {
// 循环体
}
对象属性遍历
const person = {
name: 'John',
age: 30,
city: 'New York'
};
// 遍历对象属性
for (const key in person) {
console.log(`${key}: ${person[key]}`);
}
// 安全遍历(过滤继承属性)
for (const key in person) {
if (person.hasOwnProperty(key)) {
console.log(`${key}: ${person[key]}`);
}
}
// 现代方法:使用Object.keys()
Object.keys(person).forEach(key => {
console.log(`${key}: ${person[key]}`);
});
数组遍历注意事项
const arr = ['a', 'b', 'c'];
arr.customProperty = 'custom';
// for...in会遍历所有可枚举属性(包括非数字索引)
for (const index in arr) {
console.log(`${index}: ${arr[index]}`);
}
// 输出: 0: a, 1: b, 2: c, customProperty: custom
// 对于数组,推荐使用for循环或for...of
for (let i = 0; i < arr.length; i++) {
console.log(`${i}: ${arr[i]}`);
}
// 输出: 0: a, 1: b, 2: c
for…of 循环
基本语法
for (变量 of 可迭代对象) {
// 循环体
}
数组遍历
const fruits = ['apple', 'banana', 'orange'];
// 遍历数组值
for (const fruit of fruits) {
console.log(fruit);
}
// 同时获取索引和值
for (const [index, fruit] of fruits.entries()) {
console.log(`${index}: ${fruit}`);
}
// 过滤遍历
for (const fruit of fruits) {
if (fruit.startsWith('a')) {
console.log(`以a开头的水果: ${fruit}`);
}
}
字符串遍历
const text = "Hello";
for (const char of text) {
console.log(char); // H e l l o
}
// 处理Unicode字符
const emoji = "👨👩👧👦";
for (const char of emoji) {
console.log(char); // 正确处理复合Unicode字符
}
其他可迭代对象
// Set遍历
const uniqueNumbers = new Set([1, 2, 3, 2, 1]);
for (const num of uniqueNumbers) {
console.log(num); // 1, 2, 3
}
// Map遍历
const userRoles = new Map([
['john', 'admin'],
['jane', 'user'],
['bob', 'moderator']
]);
for (const [user, role] of userRoles) {
console.log(`${user}: ${role}`);
}
// NodeList遍历
const elements = document.querySelectorAll('.item');
for (const element of elements) {
element.style.color = 'red';
}
循环控制语句
break语句
// 跳出循环
for (let i = 0; i < 10; i++) {
if (i === 5) {
break; // 当i=5时跳出循环
}
console.log(i); // 输出: 0, 1, 2, 3, 4
}
// 在嵌套循环中使用标签
outerLoop: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) {
break outerLoop; // 跳出外层循环
}
console.log(`i=${i}, j=${j}`);
}
}
continue语句
// 跳过当前迭代
for (let i = 0; i < 5; i++) {
if (i === 2) {
continue; // 跳过i=2的迭代
}
console.log(i); // 输出: 0, 1, 3, 4
}
// 过滤偶数
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) {
continue; // 跳过偶数
}
console.log(`奇数: ${i}`);
}
循环性能优化
1. 缓存数组长度
// 低效:每次迭代都计算长度
for (let i = 0; i < arr.length; i++) {
// 处理arr[i]
}
// 高效:缓存长度
for (let i = 0, len = arr.length; i < len; i++) {
// 处理arr[i]
}
2. 选择合适的循环类型
const numbers = [1, 2, 3, 4, 5];
// 需要索引:使用传统for循环
for (let i = 0; i < numbers.length; i++) {
console.log(`索引${i}: ${numbers[i]}`);
}
// 只需要值:使用for...of
for (const num of numbers) {
console.log(num);
}
// 函数式方法:更简洁但可能略慢
numbers.forEach((num, index) => {
console.log(`索引${index}: ${num}`);
});
3. 避免在循环中创建函数
// 低效:在循环中创建函数
for (let i = 0; i < 1000; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
// 高效:在循环外定义函数
function logNumber(num) {
console.log(num);
}
for (let i = 0; i < 1000; i++) {
setTimeout(() => logNumber(i), 100);
}
现代循环方法
数组方法替代循环
const numbers = [1, 2, 3, 4, 5];
// forEach: 遍历每个元素
numbers.forEach((num, index) => {
console.log(`索引${index}: ${num}`);
});
// map: 转换数组
const doubled = numbers.map(num => num * 2);
// filter: 过滤数组
const evenNumbers = numbers.filter(num => num % 2 === 0);
// find: 查找元素
const found = numbers.find(num => num > 3);
// some/every: 测试条件
const hasEven = numbers.some(num => num % 2 === 0);
const allPositive = numbers.every(num => num > 0);
// reduce: 累积操作
const sum = numbers.reduce((total, num) => total + num, 0);
最佳实践
1. 选择合适的循环类型
- for: 需要精确控制循环过程
- while: 条件复杂或不确定循环次数
- do-while: 至少执行一次循环体
- for…in: 遍历对象属性
- for…of: 遍历可迭代对象的值
2. 避免无限循环
// 添加防护措施
let attempts = 0;
const maxAttempts = 1000;
while (condition && attempts < maxAttempts) {
// 循环体
attempts++;
}
if (attempts === maxAttempts) {
console.warn('循环达到最大尝试次数');
}
3. 及时跳出循环
// 找到目标后立即跳出
function findUser(users, targetId) {
for (const user of users) {
if (user.id === targetId) {
return user; // 找到后立即返回
}
}
return null; // 未找到
}
4. 使用描述性的变量名
// 不好
for (let i = 0; i < arr.length; i++) {
// 处理arr[i]
}
// 更好
for (let userIndex = 0; userIndex < users.length; userIndex++) {
const currentUser = users[userIndex];
// 处理currentUser
}
// 最好(使用for...of)
for (const currentUser of users) {
// 处理currentUser
}