JavaScript循环需据数据结构、索引需求、提前退出及异步上下文选择:for控制最细但易错边界;while/do...while需防卡顿;关键在判断中断、await、重复注册与遍历中修改集合等细节。

JavaScript 循环不是“选一个用就行”,而是得看数据结构、是否需要索引、是否要提前退出、是否在异步上下文中——选错循环类型,轻则逻辑出错,重则阻塞主线程或漏掉 Promise。
for 循环:控制最细,但容易写错边界
适用于已知迭代次数、需要精确控制初始化/条件/更新三部分的场景,比如遍历数组索引、实现算法逻辑(如冒泡排序)。
常见错误是 i 导致越界,或把 i++ 写成 i+ 之类拼写错误;另外在闭包中直接用 i 会捕获最后值,需用 let 声明或立即执行函数包裹。
示例:
立即学习“Java免费学习笔记(深入)”;
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
注意:for 不适合遍历对象属性(无序且含原型链),也不适合遍历 Map 或 Set(虽可行但不语义化)。
for...of 循环:遍历可迭代对象的首选
用于遍历 Array、String、TypedArray、Map、Set、Generator 等实现了 [Symbol.iterator] 的对象。它拿的是值,不是索引。
不能直接拿到下标,如需索引得配合 entries();也不能遍历普通对象(会报 TypeError: obj is not iterable)。
示例:
立即学习“Java免费学习笔记(深入)”;
for (const item of arr) {
console.log(item); // 直接是元素值
}
for (const [index, value] of arr.entries()) {
console.log(index, value); // 需显式调用 entries()
}
for...in 循环:只该用来遍历对象键名,且必须加 hasOwnProperty 检查
for...in 遍历的是对象自身 + 原型链上所有**可枚举字符串键**,顺序不保证,对数组使用会把数字索引当字符串,还可能遍历到意外的原型方法(如 arr.toString)。
唯一合理使用场景是调试时快速查看对象自有属性,生产环境务必搭配 Object.prototype.hasOwnProperty.call(obj, key) 过滤。
示例:
立即学习“Java免费学习笔记(深入)”;
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
console.log(key, obj[key]);
}
}
别用它遍历数组,也别用它处理 Map / Set ——它们没有可枚举属性。
while 和 do...while:适合条件驱动、非固定长度的循环
当迭代次数完全取决于运行时状态(比如等待某个 DOM 元素出现、读取流数据直到 null、实现状态机)时,while 比 for 更自然。
关键点:必须确保循环体内有能改变条件的语句,否则死循环;do...while 至少执行一次,适合“先操作再判断”的场景(如用户输入校验)。
示例:
立即学习“Java免费学习笔记(深入)”;
let el = document.getElementById('target');
while (!el) {
await new Promise(r => setTimeout(r, 10));
el = document.getElementById('target');
}
// do...while 示例:确保至少提示一次
let input;
do {
input = prompt('请输入非空内容');
} while (!input || input.trim() === '');
注意:while 在处理大量同步计算时易导致页面卡顿,必要时应拆分任务或用 setTimeout / queueMicrotask 让出主线程。
真正难的不是记住语法,而是判断「这个循环会不会被中断?是否要 await?是否在事件回调里重复注册?是否修改了正在遍历的集合?」——这些细节比循环类型本身更容易引发 bug。











