应优先用for循环而非forEach(),因for支持break/continue、反向遍历、类数组操作且能提前终止;forEach()无法中断遍历、不支持类数组、return仅退出当前回调。

JavaScript 中没有“必须用哪种循环”的硬性规定,关键看你要遍历什么、是否需要中途退出、是否要处理异步逻辑。常见的 for、while、for...of、for...in、forEach() 各有明确边界,混用或误用会直接导致 bug 或性能问题。
什么时候该用 for 循环而不是 forEach()
forEach() 看起来简洁,但它不能 break 或 continue,也不能用 return 中断整个遍历;而原生 for 循环完全可控。如果你需要在满足某个条件时立刻跳出(比如查找第一个匹配项),for 是唯一可靠选择。
- 查找数组中第一个大于 10 的数:用
for可以break,用forEach()只能靠抛错或额外标记位,不直观也不高效 -
forEach()的回调函数中return只是退出当前回调,不影响后续迭代 -
for支持反向遍历(i--),forEach()固定从头到尾 - 对类数组对象(如
arguments、NodeList)操作时,forEach()可能不支持,而for总是可用
const arr = [2, 5, 12, 8];
let found = null;
for (let i = 0; i < arr.length; i++) {
if (arr[i] > 10) {
found = arr[i];
break; // ✅ 有效终止
}
}
for...in 和 for...of 别搞混:一个遍历键,一个遍历值
for...in 遍历的是对象的**可枚举属性名(字符串)**,包括原型链上的;for...of 遍历的是**可迭代对象的实际值**(如数组元素、Set 成员、字符串字符),它依赖 [Symbol.iterator]。拿数组举例:for...in 给你的是索引("0", "1"),for...of 给你的是元素(10, 20)。
- 永远不要用
for...in遍历数组——它不保证顺序,还会遍历手动添加的非数字属性 -
for...in对空数组也会执行(如果原型上有可枚举属性);for...of不会 - 普通对象默认不可迭代,
for...of直接报错TypeError: obj is not iterable;此时应改用Object.keys()+for...of或for...in
const arr = [10, 20];
arr.custom = 'hello';
for (const key in arr) {
console.log(key); // "0", "1", "custom" —— ❌ 不是纯数组遍历
}
for (const value of arr) {
console.log(value); // 10, 20 —— ✅ 纯元素值
}
异步循环里用 var 声明 i 会导致所有回调共享同一个 i
这是闭包和变量提升的经典坑。用 var 声明的循环变量是函数作用域,在异步回调中访问时,i 已经变成终值(比如 arr.length)。解决方法只有两个:用 let(块级作用域),或用立即执行函数包裹 var i。
立即学习“Java免费学习笔记(深入)”;
-
let是最直接的解法,每次迭代都绑定独立的i - 用
forEach()也天然规避此问题,因为它的参数是函数参数,不是变量提升目标 -
for...of同样安全,因为它的遍历变量也是块级绑定 - 千万别在
setTimeout或fetch().then()里直接引用var i
const arr = ['a', 'b', 'c'];
for (var i = 0; i < arr.length; i++) {
setTimeout(() => console.log(i), 100); // 输出 3, 3, 3 —— ❌
}
for (let i = 0; i < arr.length; i++) {
setTimeout(() => console.log(i), 100); // 输出 0, 1, 2 —— ✅
}
真正难的不是记住语法,而是判断「这个数据结构是否可迭代」「这次遍历要不要提前退出」「回调里访问的变量生命周期是否匹配」。这些判断一旦出错,bug 往往延迟暴露,调试成本远高于写的时候多想两秒。











