迭代器模式核心概念

  1. 迭代器模式 (Iterator Pattern)
    1. 是一种行为型设计模式,它提供一种方法顺序访问一个聚合对象 (如数组、集合、链表等) 中的各个元素,而 不暴露该对象的内部表示
    2. 简单来说,就是把 “遍历集合元素” 的逻辑从集合对象中抽离出来,交给专门的迭代器对象处理,让集合只专注于存储数据,迭代器专注于遍历数据;
  2. 类图:
  3. 实现代码:
    // ---------------------- 迭代器接口(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 迭代器(最常见)

  1. 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
    }
    

框架 / 库中的迭代器应用

  1. ReactReact.Children 提供的 forEach/map 方法,底层通过迭代器遍历虚拟 DOM 节点 (隐藏了 Children 的内部结构)

  2. Redux:中间件遍历、state 遍历逻辑常使用迭代器模式;

  3. RxJS:响应式编程中的数据流遍历,核心是迭代器模式的扩展;

  4. UI 组件库:如表格组件的行数据遍历、树形组件的节点遍历,通过迭代器统一遍历逻辑,适配不同数据格式 (数组、树形结构)

异步迭代器(ES2018 Async Iterator)

  1. 前端处理异步数据流 (如接口分页请求、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 → ...
        // 比如这里可以渲染一条数据、处理一条数据,无需等全部加载
      }
    })();
    

富文本编辑器:遍历文档节点树

  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 状态树

*组件库:遍历组件树查找特定组件

迭代器模式的优缺点

优点

  1. 单一职责原则:将遍历行为封装在独立对象中;

  2. 开闭原则:可以添加新的迭代器类型而不修改聚合类;

  3. 统一接口:提供统一的遍历方式;

  4. 支持多种遍历方式:可以实现不同算法遍历同一聚合对象;

缺点

  1. 增加复杂度:引入额外的类和接口;

  2. 开销较大:对于简单集合可能过度设计;

打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

你好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 迭代器模式核心概念
  2. 2. 常用使用场景
    1. 2.1. 原生 JavaScript 迭代器(最常见)
    2. 2.2. 框架 / 库中的迭代器应用
    3. 2.3. 异步迭代器(ES2018 Async Iterator)
    4. 2.4. 富文本编辑器:遍历文档节点树
    5. 2.5. *数据可视化:遍历图表数据点
    6. 2.6. *文件浏览器:遍历文件目录结构
    7. 2.7. *状态管理:遍历 Redux/Vuex 状态树
    8. 2.8. *组件库:遍历组件树查找特定组件
  3. 3. 迭代器模式的优缺点
    1. 3.1. 优点
    2. 3.2. 缺点