Skip to content

迭代器模式

行为型模式,普通的 for 循环不属于迭代器模式

特点

迭代器模式的核心在于抽象遍历过程,使得客户端无需关心底层数据结构的具体实现。

  • 顺序访问有序结构(如数组,NodeList)
  • 不知道数据的长度和内容结构
  • 高内聚、低耦合

代码

ts
class DataIterator {
  private data: number[];
  private index = 0;

  constructor(container: DataContainer) {
    this.data = container.data;
  }

  next(): number | null {
    if (this.hasNext()) {
      return this.data[this.index++]; // 返回下一个值,并将索引指向下一个
    }
    return null;
  }

  hasNext() {
    if (this.index >= this.data.length) return false;
    return true;
  }
}

class DataContainer {
  data = [10, 20, 30, 40, 50];
  getIterator() {
    // 返回一个迭代器对象
    return new DataIterator(this);
  }
}

const container = new DataContainer();
const iterator = container.getIterator();

while (iterator.hasNext()) {
  console.log(iterator.next());
}

使用 UML 图表示

alt text

下面看一下使用场景

有序结构

有序结构是迭代器模式的主要应用场景,以下是一些常见的有序结构:

  • 字符串
  • 数组(最常见的有序结构,迭代器模式可以统一遍历方式)
  • NodeList 等 DOM 集合
  • Map
  • Set
  • 函数的 arguments 对象

Symbol.iterator 和迭代器

Symbol.iterator 是 JavaScript 中实现迭代器模式的标准化接口。

  • 所有有序对象,都内置了 Symbol.iterator 方法
  • 执行该方法,会返回一个迭代器对象
js
const arr = [10, 20, 30];
const iterator = arr[Symbol.iterator]();
iterator.next();
  • 模拟 Symbol.iterator 方法
ts
interface InteratorRes {
  value: number | undefined;
  done: boolean;
}

class CustomIterator {
  private length = 3;
  private index = 0;

  next(): InteratorRes {
    this.index++;
    if (this.index <= this.length) {
      return {
        value: this.index,
        done: false,
      };
    }
    return {
      value: undefined,
      done: true,
    };
  }

  [Symbol.iterator]() {
    return this;
  }
}

const iterator = new CustomIterator();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

迭代器的作用

  • 用于 for...of 循环(对比 for...in)
js
for (let n of iterator) {
  console.log(n);
}
  • 数组:解构,扩展操作符,Array.from()
js
// 解构
const [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1 2 3

// 扩展操作符
const arr = [1, 2, 3];
const newArr = [...arr, 4, 5];
console.log(newArr); // [1, 2, 3, 4, 5]

// Array.from()
const arrayLike = { 0: 'a', 1: 'b', length: 2 };
const arrFrom = Array.from(arrayLike);
console.log(arrFrom); // ['a', 'b']
  • 用于创建 Map 和 Set 等数据结构
  • 用于 Promise.all 和 Promise.race 等方法
js
const arr = [1, 2, 3];
const map = new Map(arr);
Promise.all(map);
  • 用于 yield* 语句

Generator 生成器

基本使用

ts
function* genNums() {
  yield 10;
  yield 20;
  yield 30;
}

const numsItr = genNums(); // 迭代器,类似 arr[Symbol.iterator]()
// console.log(numsItr.next());
// console.log(numsItr.next());
// console.log(numsItr.next());
// console.log(numsItr.next());
for (const num of numsItr) {
  console.log(num);
}

yield* 语句

ts
function* genNums() {
  yield* [10, 20, 30]; // * 后可以带有序结构,已经实现 [Symbol.iterator]

  // 或者
  //   for(const num of [10,20,30]){
  //     yield num;
  //   }
}

const numsItr = genNums(); // 迭代器,类似 arr[Symbol.iterator]()
console.log(numsItr.next());
console.log(numsItr.next());
console.log(numsItr.next());
console.log(numsItr.next());
// for(const num of numsItr){
//   console.log(num);
// }

改造上面的 CustomIterator 方法

ts
class CustomIterator {
  private data: number[];
  constructor() {
    this.data = [10, 20, 30, 40, 50];
  }

  *[Symbol.iterator]() {
    yield* this.data;
  }
}

const iterator = new CustomIterator();
for (const n of iterator) {
  console.log(n);
}

Generator + yiled 遍历 DOM 树

ts
function* traverse(elList: Element[]): any {
  for (const el of elList) {
    yield el;

    const children = Array.from(el.children);
    if (children.length) {
      yield* traverse(children);
    }
  }
}

const elList = document.getElementById('container');
if (elList) {
  for (const el of traverse([elList])) {
    console.log(el);
  }
}

基于 MIT 许可发布