迭代器模式核心概念
- 迭代器模式 (Iterator Pattern):
- 是一种行为型设计模式,它提供一种方法顺序访问一个聚合对象 (如数组、集合、链表等) 中的各个元素,而 不暴露该对象的内部表示;
- 简单来说,就是把 “遍历集合元素” 的逻辑从集合对象中抽离出来,交给专门的迭代器对象处理,让集合只专注于存储数据,迭代器专注于遍历数据;
- 类图:
- 实现代码:
// ---------------------- 迭代器接口(Iterator)---------------------- interface Iterator { /** 判断是否还有下一个元素 */ hasNext(): boolean; /** 获取下一个元素 */ next(): any; } // ---------------------- 聚合接口(Aggregate)---------------------- interface Aggregate { /** 创建迭代器 */ createIterator(): Iterator; } // ---------------------- 具体迭代器(ConcreteIterator)---------------------- class ConcreteIterator implements Iterator { // 持有具体聚合对象的引用(类图中的关联关系) private aggregate: ConcreteAggregate; // 当前遍历的索引(类图中的私有属性) private currentIndex: number = 0; constructor(aggregate: ConcreteAggregate) { this.aggregate = aggregate; } // 实现判断是否有下一个元素的方法 hasNext(): boolean { return this.currentIndex < this.aggregate.getCount(); } // 实现获取下一个元素的方法 next(): any { if (this.hasNext()) { return this.aggregate.getItem(this.currentIndex++); } return null; } } // ---------------------- 具体聚合(ConcreteAggregate)---------------------- class ConcreteAggregate implements Aggregate { // 存储元素的数组(类图中的私有属性) private items: any[] = []; constructor(items: any[]) { this.items = items; } // 实现创建迭代器的方法(返回具体迭代器实例) createIterator(): Iterator { return new ConcreteIterator(this); } // 获取指定索引的元素(给迭代器调用) getItem(index: number): any { return this.items[index]; } // 获取元素总数(给迭代器调用) getCount(): number { return this.items.length; } } // ---------------------- 测试代码 ---------------------- // 1. 创建具体聚合对象(存储一组数据) const fruitAggregate = new ConcreteAggregate(["苹果", "香蕉", "橙子", "葡萄"]); // 2. 创建迭代器 const iterator = fruitAggregate.createIterator(); // 3. 使用迭代器遍历元素 console.log("遍历水果集合:"); while (iterator.hasNext()) { console.log(iterator.next()); // 依次输出:苹果、香蕉、橙子、葡萄 }
常用使用场景
原生 JavaScript 迭代器(最常见)
-
ES6 引入的 Iterator 规范是迭代器模式的原生实现,所有具备
[Symbol.iterator]()方法的对象 (如数组、字符串、Set、Map、Generator) 都遵循迭代器模式:// 数组的原生迭代器(底层就是迭代器模式) const arr = [1, 2, 3]; 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 } // for...of 循环本质是调用迭代器的 next() 方法 for (const item of arr) { console.log(item); // 1、2、3 }
框架 / 库中的迭代器应用
-
React:React.Children 提供的 forEach/map 方法,底层通过迭代器遍历虚拟 DOM 节点 (隐藏了 Children 的内部结构);
-
Redux:中间件遍历、state 遍历逻辑常使用迭代器模式;
-
RxJS:响应式编程中的数据流遍历,核心是迭代器模式的扩展;
-
UI 组件库:如表格组件的行数据遍历、树形组件的节点遍历,通过迭代器统一遍历逻辑,适配不同数据格式 (数组、树形结构);
异步迭代器(ES2018 Async Iterator)
-
前端处理异步数据流 (如接口分页请求、WebSocket 消息、文件流) 时,异步迭代器是迭代器模式的异步扩展:
async function onlyAsyncAwait() { let page = 1; const allData = []; // 必须先收集所有数据 while (page <= 3) { const res = await Promise.resolve({ data: [`第${page}页数据1`, `第${page}页数据2`] }); allData.push(...res.data); // 一次性收集所有分页数据 page++; } return allData; // 只能一次性返回所有数据 } // 调用结果:必须等所有分页请求完成,才能拿到全部数据 onlyAsyncAwait().then(allData => { console.log(allData); // 一次性输出:["第1页数据1", "第1页数据2", "第2页数据1", ...] // 想要“遍历”只能自己手动循环,且数据已全部加载到内存 });async function* asyncPaginationIterator() { let page = 1; while (page <= 3) { // async/await:等待当前分页的异步请求完成 const res = await Promise.resolve({ data: [`第${page}页数据1`, `第${page}页数据2`] }); // yield*:分步输出当前页的数据(执行到这里暂停,下次调用再继续) yield* res.data; page++; } } // 调用方式:异步迭代器,分步消费数据 (async () => { // for await...of:每次迭代才触发下一页的请求/数据输出 for await (const item of asyncPaginationIterator()) { console.log(item); // 分步输出:第1页数据1 → 第1页数据2 → 第2页数据1 → ... // 比如这里可以渲染一条数据、处理一条数据,无需等全部加载 } })();
富文本编辑器:遍历文档节点树
-
DOM 元素遍历迭代器,可以根据 dfs 或者 bfs 的方式遍历;
// DOM 树节点迭代器 class DOMNodeIterator implements Iterator<HTMLElement> { private nodes: HTMLElement[] = []; private index: number = 0; constructor(root: HTMLElement, traversalType: 'DFS' | 'BFS' = 'DFS') { if (traversalType === 'DFS') { this.dfs(root); } else { this.bfs(root); } } private dfs(node: HTMLElement): void { this.nodes.push(node); Array.from(node.children).forEach(child => { this.dfs(child as HTMLElement); }); } private bfs(root: HTMLElement): void { const queue: HTMLElement[] = [root]; while (queue.length > 0) { const node = queue.shift()!; this.nodes.push(node); queue.push(...Array.from(node.children) as HTMLElement[]); } } first(): HTMLElement | null { this.index = 0; return this.nodes[0] || null; } next(): HTMLElement | null { this.index++; return this.nodes[this.index] || null; } hasNext(): boolean { return this.index < this.nodes.length - 1; } current(): HTMLElement | null { return this.nodes[this.index] || null; } // 额外方法:过滤特定类型的元素 filterByTagName(tagName: string): HTMLElement[] { return this.nodes.filter(node => node.tagName.toLowerCase() === tagName.toLowerCase() ); } } // 使用示例 // 在浏览器环境中 // const root = document.getElementById('app')!; // const iterator = new DOMNodeIterator(root, 'DFS'); // // let node = iterator.first(); // while (node) { // console.log(node.tagName, node.className); // node = iterator.next(); // }
*数据可视化:遍历图表数据点
*文件浏览器:遍历文件目录结构
*状态管理:遍历 Redux/Vuex 状态树
*组件库:遍历组件树查找特定组件
迭代器模式的优缺点
优点
-
单一职责原则:将遍历行为封装在独立对象中;
-
开闭原则:可以添加新的迭代器类型而不修改聚合类;
-
统一接口:提供统一的遍历方式;
-
支持多种遍历方式:可以实现不同算法遍历同一聚合对象;
缺点
-
增加复杂度:引入额外的类和接口;
-
开销较大:对于简单集合可能过度设计;
观察者模式(行为型模式)
上一篇