🎯 学习目标:全面掌握 JavaScript 的迭代语句与迭代协议,能在不同数据结构与同步/异步场景下选择正确的遍历方式,并写出高性能、可维护的代码。
📊 难度等级:中级
🏷️ 技术标签:
#JavaScript#迭代器#生成器#for-of#for-in#for await...of⏱️ 阅读时间:约9分钟
🌟 引言
在日常 JavaScript 开发中,遍历数据"看起来都差不多",但一旦涉及对象、Map/Set、类数组、异步数据流、性能优化,就容易踩坑:
- 数组用 
for...in导致顺序和性能问题; - 把对象当可迭代结构用 
for...of直接报错; - 自定义迭代器和生成器不会用,错过优雅的惰性计算;
 - 大数据流用 
for await...of才是正确姿势,却不熟悉协议细节。 
今天我用 7 个核心技巧,系统讲清"语句与协议"的边界与最佳实践,帮你写出更高效、可控的迭代代码。
💡 核心技巧详解
1. for vs while:基础循环的选择
🔍 应用场景
索引驱动的数组遍历、需要精细控制起止与步长、或在条件驱动下执行循环。
❌ 常见问题
用 while 忘记更新计数器,或数组越界导致死循环/错误。
            
            
              js
              
              
            
          
          // ❌ 计数器更新缺失可能导致死循环
let i = 0;
while (i < 3) {
  console.log('loop:', i);
  // 缺失 i++
}
        ✅ 推荐方案
            
            
              js
              
              
            
          
          /**
 * 使用 for 循环安全遍历数组
 * @param {any[]} list - 任意元素数组
 * @returns {any[]} 同步收集的结果
 */
const traverseWithFor = (list) => {
  const results = [];
  for (let i = 0; i < list.length; i++) {
    // 索引明确,性能稳定
    results.push(list[i]);
  }
  return results;
};
/**
 * 使用 while 循环在条件驱动下遍历
 * @param {number} start - 起始值
 * @param {number} end - 结束值(不含)
 * @returns {number[]} 生成的序列
 */
const traverseWithWhile = (start, end) => {
  const seq = [];
  let i = start;
  while (i < end) {
    seq.push(i);
    i += 1; // 确保条件推进
  }
  return seq;
};
        💡 核心要点
for适合索引明确的顺序遍历;while适合条件驱动的循环,注意推进条件;- 数据量大时优先 
for,可微调步长与边界以获得稳定性能。 
2. for...of vs for...in:遍历语义不要混用
🔍 应用场景
for...of 用于"可迭代对象"(数组、字符串、Map、Set、生成器等);for...in 用于对象的"可枚举属性键"。
❌ 常见问题
对数组使用 for...in 导致遍历到原型属性、顺序不稳定;对普通对象使用 for...of 直接报错。
            
            
              js
              
              
            
          
          // ❌ 对数组使用 for...in(遍历索引字符串,顺序可能受影响)
const arr = [10, 20, 30];
for (const k in arr) {
  console.log('index string:', k); // '0', '1', '2'
}
// ❌ 对对象使用 for...of(非可迭代,抛错)
// for (const v of { a: 1 }) {} // TypeError: {} is not iterable
        ✅ 推荐方案
            
            
              js
              
              
            
          
          /**
 * 遍历可迭代对象(数组/字符串/Map/Set)
 * @param {Iterable<any>} iterable - 可迭代对象
 * @returns {any[]} 收集到的值
 */
const collectIterableValues = (iterable) => {
  const values = [];
  for (const v of iterable) {
    values.push(v);
  }
  return values;
};
/**
 * 遍历对象自有可枚举属性键
 * @param {Record<string, any>} obj - 普通对象
 * @returns {string[]} 键列表
 */
const collectOwnKeys = (obj) => {
  const keys = [];
  for (const k in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, k)) keys.push(k);
  }
  return keys;
};
        💡 核心要点
for...of读取"值";for...in读取"键";- 对象遍历更推荐 
Object.keys/values/entries保持可控与可读; - 避免对数组使用 
for...in,不稳定且性能较差。 
3. 迭代协议:可迭代与迭代器
🔍 应用场景
自定义数据结构的迭代行为,支持惰性求值与可控遍历。
✅ 推荐方案
            
            
              js
              
              
            
          
          /**
 * 自定义可迭代范围 [start, end)
 * @param {number} start - 起始
 * @param {number} end - 结束(不含)
 * @returns {Iterable<number>} 可迭代范围
 */
const createRange = (start, end) => ({
  [Symbol.iterator]: () => {
    let i = start;
    return {
      next: () => (i < end ? { value: i++, done: false } : { value: undefined, done: true })
    };
  }
});
        💡 核心要点
- 可迭代对象需实现 
[Symbol.iterator]返回迭代器; - 迭代器需实现 
next()返回{ value, done }; - 惰性迭代避免一次性创建大量中间数据,利于性能与内存。
 
4. 生成器(Generator):更优雅的迭代器写法
🔍 应用场景
用更简洁的语法生成迭代序列,支持 yield 惰性输出与中断。
            
            
              js
              
              
            
          
          /**
 * 生成器创建斐波那契序列(前 n 个)
 * @param {number} n - 个数
 * @returns {Iterable<number>} 斐波那契序列
 */
const fibonacci = function* (n) {
  let a = 0, b = 1, i = 0;
  while (i < n) {
    yield a;
    [a, b] = [b, a + b];
    i += 1;
  }
};
        💡 核心要点
- 生成器本质是迭代器,语法更简洁;
 yield提供惰性产生值的能力;- 可搭配 
for...of直接遍历。 
5. 异步迭代与 for await...of:数据流的正确遍历
🔍 应用场景
分页加载、流式文件读写、网络请求批次处理等异步数据源。
            
            
              js
              
              
            
          
          /**
 * 异步生成器:模拟批次拉取数据
 * @param {number} batches - 批次数
 * @returns {AsyncIterable<number>} 异步可迭代数据
 */
const fetchBatches = async function* (batches) {
  for (let i = 1; i <= batches; i++) {
    await new Promise((r) => setTimeout(r, 5));
    yield i; // 每批返回一个结果
  }
};
/**
 * 使用 for await...of 收集异步迭代结果
 * @param {AsyncIterable<any>} asyncIterable - 异步可迭代
 * @returns {Promise<any[]>} 收集到的值
 */
const collectAsync = async (asyncIterable) => {
  const out = [];
  for await (const chunk of asyncIterable) {
    out.push(chunk);
  }
  return out;
};
        💡 核心要点
for await...of遍历AsyncIterable/AsyncGenerator;- 避免将 
Promise[]直接用for await...of,应先Promise.all或转为异步迭代; - 异步迭代是处理大数据流的内存友好方案。
 
6. 遍历不同数据结构:Map/Set/字符串/类数组
            
            
              js
              
              
            
          
          /**
 * 遍历 Map/Set/字符串/类数组的统一收集器
 * @param {any} target - 目标数据结构
 * @returns {any[]} 收集到的值
 */
const collectValues = (target) => {
  if (target instanceof Map) return Array.from(target.entries());
  if (target instanceof Set) return Array.from(target.values());
  if (typeof target === 'string') return Array.from(target);
  if (typeof target.length === 'number') return Array.from(target); // NodeList/HTMLCollection
  return [];
};
        💡 核心要点
Map默认遍历[key, value],Set遍历值;- 字符串是可迭代对象,可被 
for...of按字符遍历; - 类数组(如 
NodeList)可用Array.from转正,避免for...in。 
7. 性能与边界:选择正确的迭代策略
🎯 实战建议
- 大数组性能更稳定的通常是索引 
for; - 需要可读性与语义清晰,优先 
for...of; - 对象遍历优先 
Object.keys/entries,避免for...in的原型链干扰; - 大数据流与分页拉取用 
for await...of,避免一次性内存爆炸; - 迭代中修改容器(增删元素)要谨慎,优先生成快照或用惰性策略。
 
📊 技巧对比总结
| 技巧 | 使用场景 | 优势 | 注意事项 | 
|---|---|---|---|
| for | 索引驱动数组 | 性能稳定,可控 | 注意边界与步长 | 
| while | 条件驱动循环 | 灵活 | 防止死循环 | 
| for...of | 可迭代对象 | 语义清晰 | 不适用于普通对象 | 
| for...in | 对象键遍历 | 简单 | 避免用于数组,过滤原型链 | 
| 迭代协议 | 自定义结构 | 惰性、可控 | 正确实现 next() | 
| 生成器 | 简洁迭代器 | 语法优雅 | 正确使用 yield | 
| for await...of | 异步数据流 | 内存友好 | 仅用于异步可迭代 | 
🎯 实战应用建议
最佳实践
- 使用 
for...of遍历集合类型,提升语义与可读性。 - 对象遍历使用 
Object.entries与for...of组合处理键值对。 - 大数据流统一封装为 
AsyncGenerator,用for await...of消费。 - 根据性能需求选择 
for或for...of,避免对数组使用for...in。 
性能考虑
- 大数组在热路径中优先 
for; - 遍历中避免闭包捕获大对象,及时释放引用;
 - 生成器/异步生成器的惰性策略可显著降低内存峰值。
 
💡 总结
这 7 个迭代技巧覆盖了"语句与协议"的完整谱系:从 for/while 到 for...of/for...in,再到迭代器/生成器与异步迭代。掌握它们后,你的代码将:
- 更语义化,遍历意图清晰;
 - 更高性能,避免不必要的中间数据与错误用法;
 - 更易维护,统一封装迭代行为并复用。
 
🔗 相关资源
- MDN:语句和声明 --- 迭代分节(for、for...in、for...of、for await...of、while、do...while) developer.mozilla.org/zh-CN/docs/...
 
💡 今日收获:理解迭代语句与协议的边界,选择合适的遍历方式,写出可读、可控、性能稳定的迭代代码。
如果这篇文章对你有帮助,欢迎点赞、收藏和分享!有任何问题也欢迎在评论区讨论。 🚀