JS如何实现迭代器?迭代器协议

JS如何实现迭代器?迭代器协议
最新回答
唇诱

2026-10-30 07:49:30

JavaScript实现迭代器的核心是遵循“可迭代协议”和“迭代器协议”,通过定义[Symbol.iterator]方法返回具备next()方法的迭代器对象,从而支持for...of循环和展开运算符(...)。以下是具体实现方法与关键要点:

一、迭代器协议与可迭代协议的实现
  • 可迭代协议:对象需实现[Symbol.iterator]方法,该方法返回一个迭代器对象。
  • 迭代器协议:迭代器对象需具备next()方法,每次调用返回{ value: T, done: boolean }对象,其中value为当前值,done表示遍历是否完成。

示例:自定义Range对象

class MyRange { constructor(start, end) { this.start = start; this.end = end; } [Symbol.iterator]() { let current = this.start; const end = this.end; return { next() { if (current <= end) { return { value: current++, done: false }; } else { return { value: undefined, done: true }; } } }; }}const myNumbers = new MyRange(1, 5);for (const num of myNumbers) { console.log(num); // 输出:1, 2, 3, 4, 5}console.log([...myNumbers]); // 输出:[1, 2, 3, 4, 5]
  • 关键点:MyRange通过[Symbol.iterator]返回匿名迭代器对象,维护current状态,实现惰性求值。
二、迭代器的核心价值
  1. 统一遍历接口

    迭代器协议为所有可遍历数据结构(如数组、字符串、Set、Map及自定义对象)提供标准化接口,消除不同数据结构的遍历差异。

    痛点解决:避免为不同数据结构编写碎片化遍历逻辑(如数组用for、对象用for...in、Set用forEach)。

  2. 惰性求值

    仅在调用next()时计算当前值,适合处理大数据流或无限序列(如斐波那契数列),减少内存占用。

    示例:若MyRange的end值极大或未定义,未遍历的部分不会提前计算。

三、迭代器与生成器的关系
  • 生成器是迭代器的语法糖

    生成器函数(function*)自动实现迭代器协议,通过yield暂停执行并返回值,简化手动维护状态和next()逻辑。

    示例:function* simpleGenerator() { yield 1; yield 2; yield 3;}const gen = simpleGenerator();console.log(gen.next()); // { value: 1, done: false }console.log([...simpleGenerator()]); // [1, 2, 3]

  • 选择依据

    优先使用生成器:日常开发中,生成器代码更简洁、易读,适合大多数自定义迭代逻辑。

    手动实现迭代器:需极致性能优化或精细控制迭代过程时(如复杂状态管理或非线性数据结构)。

四、复杂数据结构中的高级技巧
  1. 特定遍历策略的迭代器

    树的中序遍历:通过栈模拟递归,实现惰性求值。class InOrderTreeIterator { constructor(root) { this.stack = []; this._pushLeft(root); } _pushLeft(node) { while (node) { this.stack.push(node); node = node.left; } } next() { if (this.stack.length === 0) return { value: undefined, done: true }; const node = this.stack.pop(); this._pushLeft(node.right); return { value: node.value, done: false }; }}class BinaryTree { constructor(root) { this.root = root; } [Symbol.iterator]() { return new InOrderTreeIterator(this.root); }}console.log([...tree]); // 输出中序遍历结果

  2. 链式或组合迭代器

    过滤迭代器:通过生成器函数实现惰性过滤。function* filterIterable(iterable, predicate) { for (const item of iterable) { if (predicate(item)) yield item; }}const evenNumbers = filterIterable([1, 2, 3, 4], n => n % 2 === 0);console.log([...evenNumbers]); // [2, 4]

  3. 无限序列的迭代器

    斐波那契数列:生成器无限生成序列,避免内存溢出。function* fibonacciSequence() { let a = 0, b = 1; while (true) { yield a; [a, b] = [b, a + b]; }}const fib = fibonacciSequence();console.log(fib.next().value); // 0console.log(fib.next().value); // 1

五、总结
  • 标准化与通用性:迭代器协议统一了数据结构的遍历接口,提升代码可维护性。
  • 性能优化:惰性求值减少内存占用,适合大数据或无限序列场景。
  • 灵活性:生成器简化迭代器实现,而手动迭代器提供更精细的控制能力。
  • 应用场景:复杂数据结构(如树、图)、数据流处理、函数式编程组合操作等。

通过理解并应用迭代器模式,可显著提升JavaScript代码的优雅性与效率,尤其在处理复杂遍历逻辑时优势明显。