您现在的位置是:网站首页 > 迭代器模式(Iterator)与生成器函数的关系文章详情
迭代器模式(Iterator)与生成器函数的关系
陈川
【
JavaScript
】
25062人已围观
4287字
迭代器模式是一种行为设计模式,它提供一种方法顺序访问聚合对象中的各个元素,而又不暴露其底层表示。生成器函数是JavaScript中实现迭代器的语法糖,两者在遍历数据集合时存在紧密的协同关系。
迭代器模式的核心概念
迭代器模式由两个核心部分组成:
- 可迭代对象(Iterable):实现
[Symbol.iterator]()
方法的对象 - 迭代器(Iterator):具有
next()
方法的对象,返回{value, done}
结构
// 自定义迭代器示例
class Counter {
constructor(limit) {
this.limit = limit
}
[Symbol.iterator]() {
let count = 1
return {
next: () => ({
value: count,
done: count++ > this.limit
})
}
}
}
const counter = new Counter(3)
for (const num of counter) {
console.log(num) // 1, 2, 3
}
生成器函数的本质
生成器函数(Generator Function)是ES6引入的特殊函数,通过function*
语法定义。其执行会返回一个生成器对象,该对象同时符合可迭代协议和迭代器协议:
function* numberGen() {
yield 1
yield 2
yield 3
}
const gen = numberGen()
console.log(gen.next()) // {value: 1, done: false}
console.log(gen[Symbol.iterator]() === gen) // true
生成器具有以下关键特性:
- 通过
yield
暂停执行并返回值 - 通过
next()
恢复执行 - 自动实现迭代器接口
两者的协同工作机制
生成器函数本质上是对迭代器模式的语法封装。比较两种实现方式:
传统迭代器模式实现:
const range = {
from: 1,
to: 3,
[Symbol.iterator]() {
return {
current: this.from,
last: this.to,
next() {
return {
value: this.current,
done: this.current++ > this.last
}
}
}
}
}
生成器函数实现:
const range = {
from: 1,
to: 3,
*[Symbol.iterator]() {
for(let i = this.from; i <= this.to; i++) {
yield i
}
}
}
生成器版本明显更简洁,因为:
- 自动维护迭代状态(current)
- 无需手动实现
next()
方法 - 控制流更直观
高级应用场景
惰性求值
生成器特别适合实现惰性计算:
function* fibonacci() {
let [prev, curr] = [0, 1]
while (true) {
yield curr
;[prev, curr] = [curr, prev + curr]
}
}
const fib = fibonacci()
console.log(fib.next().value) // 1
console.log(fib.next().value) // 1
console.log(fib.next().value) // 2
异步迭代
结合for await...of
实现异步数据流处理:
async function* asyncGenerator() {
const urls = ['/api/1', '/api/2']
for (const url of urls) {
const response = await fetch(url)
yield response.json()
}
}
(async () => {
for await (const data of asyncGenerator()) {
console.log(data)
}
})()
性能考量
虽然生成器提供了更优雅的语法,但在性能敏感场景需要注意:
- 生成器对象创建开销比普通迭代器略高
- V8引擎对生成器的优化不如常规循环
- 大量数据遍历时可能产生内存压力
// 性能对比基准测试
function testGenerator(n) {
function* gen() {
for (let i = 0; i < n; i++) yield i
}
console.time('generator')
for (const _ of gen()) {}
console.timeEnd('generator')
}
function testIterator(n) {
const iter = {
[Symbol.iterator]() {
let i = 0
return {
next: () => ({ value: i, done: i++ >= n })
}
}
}
console.time('iterator')
for (const _ of iter) {}
console.timeEnd('iterator')
}
testGenerator(1e6)
testIterator(1e6)
设计模式扩展应用
迭代器模式可以与其他模式结合:
组合模式+迭代器:
class TreeNode {
constructor(value) {
this.value = value
this.children = []
}
*[Symbol.iterator]() {
yield this.value
for (const child of this.children) {
yield* child
}
}
}
const tree = new TreeNode('root')
tree.children.push(new TreeNode('child1'))
tree.children[0].children.push(new TreeNode('grandchild'))
console.log([...tree]) // ['root', 'child1', 'grandchild']
观察者模式+迭代器:
function* createEventStream(target, eventType) {
let callback
target.addEventListener(eventType, e => {
if (callback) callback(e)
})
while (true) {
yield new Promise(resolve => callback = resolve)
}
}
const button = document.querySelector('button')
const clickStream = createEventStream(button, 'click')
;(async () => {
for await (const event of clickStream) {
console.log('Button clicked', event)
}
})()
ECMAScript规范的演进
ES2018引入的异步迭代器协议进一步完善了生成器的能力:
const asyncIterable = {
[Symbol.asyncIterator]() {
let i = 0
return {
next() {
return new Promise(resolve => {
setTimeout(() => {
resolve({ value: i++, done: i > 3 })
}, 1000)
})
}
}
}
}
;(async () => {
for await (const num of asyncIterable) {
console.log(num) // 0, 1, 2 (每秒输出一个)
}
})()
实际工程中的取舍
在真实项目中选择实现方式时需要考虑:
- 代码可读性与维护成本
- 团队成员的熟悉程度
- 运行环境对ES6特性的支持
- 性能瓶颈的实际影响
对于简单的遍历需求,直接使用数组方法可能更合适;对于复杂的数据结构或需要惰性求值的场景,生成器往往能提供更清晰的抽象。