人人都会AI编程

迭代协议与可迭代对象

更新时间:2026-07-11

在 ES6 之后,JavaScript 引入了一套统一的迭代机制,让各种数据结构都能以标准方式被遍历。这套机制由可迭代协议迭代器协议共同定义,是理解 for...of 循环、解构赋值、展开运算符等特性的基础。

可迭代协议(Iterable Protocol)

简单来说,一个对象只要实现了 [Symbol.iterator] 方法,并且该方法返回一个迭代器,那这个对象就是可迭代对象。引擎内部通过检查该方法的存在性来判断对象是否可迭代。

const arr = [1, 2, 3];
typeof arr[Symbol.iterator]; // "function"
// arr 是可迭代对象

迭代器协议(Iterator Protocol)

迭代器是具体执行遍历操作的对象,它必须提供一个 next() 方法,每次调用返回形如 { value, done } 的结果对象:

  • value:本次取出的值(仅在 donefalse 时有意义)
  • done:布尔值,true 表示遍历已结束,false 表示还有更多值
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

一旦 donetrue,后续 next() 通常也返回 done: true,代表迭代结束。

内置可迭代对象

语言中已经为很多常用结构实现了可迭代协议,你可以直接使用 for...of 或展开它们:

  • 数组:Array
  • 字符串:String
  • 类型化数组:Int8Array
  • MapSet
  • 函数的 arguments 对象
  • DOM 的 NodeList(如 document.querySelectorAll 的返回值)
const str = "hello";
for (const ch of str) {
  console.log(ch); // h, e, l, l, o
}

const set = new Set([10, 20, 30]);
console.log([...set]); // [10, 20, 30]

自定义可迭代对象

你可以让自己的对象也参与这套标准。只需要给它添加 [Symbol.iterator] 方法,返回一个遵循迭代器协议的对象即可:

const myIterable = {
  data: [10, 20, 30],
  [Symbol.iterator]() {
    let index = 0;
    const data = this.data;
    return {
      next() {
        if (index < data.length) {
          return { value: data[index++], done: false };
        } else {
          return { done: true };
        }
      }
    };
  }
};

for (const val of myIterable) {
  console.log(val); // 10, 20, 30
}
console.log([...myIterable]); // [10, 20, 30]

生成器函数也会自动返回迭代器,因此用 *yield 可以更简洁地实现可迭代协议(详见后续 Generator 小节)。

实际应用场景

迭代协议不仅仅服务于 for...of,它还驱动了以下常用语法:

  • 展开运算符 [...obj]:将可迭代对象的元素逐个展开
  • 解构赋值 [a, b] = obj:从可迭代对象中按顺序取值
  • Array.from(obj):将可迭代对象(或类数组对象)转换为真正的数组
  • 许多内置 API 也会接受可迭代对象作为参数,如 Promise.all(iterable)Map 构造函数

为什么普通对象不是可迭代的?

普通对象({})默认没有实现迭代协议,因为对象的键是无序的,按什么顺序迭代没有统一的语义——是按添加顺序,还是按键的字母序?所以 ES6 并没有强制要求,而是留给了开发者自行决定。如果确实需要遍历对象属性,可以使用 Object.keys()Object.values()Object.entries(),它们返回的都是数组(可迭代对象)。

总结:迭代协议的实用价值

迭代协议的本质是将“如何遍历数据”从“数据本身”中解耦。你不需要知道一个集合内部是数组、链表还是树结构,只要它实现了迭代协议,就能用统一的语法消费所有值。这让代码更具表达力、更通用,也为生成器、异步迭代器等高级特性铺平了道路。理解并善用迭代协议,会让你的 JavaScript 代码更加简洁和健壮。