likes
comments
collection
share

前端设计模式之迭代器模式(六)

作者站长头像
站长
· 阅读数 12
  • 统一遍历的模式
  • 用于顺序访问集合对象的元素,不需要知道集合对象的底层表示
  • ES6 之后使用迭代器(Iterator),任何结构只要具备 Symbol.iterator 属性,就可以被 for...of 循环和迭代器的 next 方法遍历
  • for...of 的背后正是对 next 方法的反复调用
  • 简单的 for 循环并不是迭代器模式,因为 for 循环需要知道对象的内部结构(长度和项)
  • forEach 就是最简易的迭代器
    • 不需要知道长度
    • 不需要知道如何获取 item
const pList = document.querySelectorAll("p");
pList.forEach(p => console.log(p));

实现迭代器模式

前端设计模式之迭代器模式(六)

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: number[] = [10, 20, 30, 40];
  getIterator() {
    return new DataIterator(this);
  }
}

const container = new DataContainer();
const iterator = container.getIterator();
while (iterator.hasNext()) {
  const num = iterator.next();
  console.log(num);
}

是否符合设计原则?

  • 使用者和目标数据分离,解耦
  • 目标数据自行控制内部迭代逻辑
  • 使用者不关心目标数据的内部结构

场景

JS 有序对象,都内置迭代器

  • 数组
  • 字符串
  • NodeList 等 DOM 集合
  • arguments
  • Set
  • Map

Symbol.iterator

  • 每个有序对象,都内置了 Symbol.iterator 属性,属性值是一个函数
  • 执行该函数讲返回 iterator 迭代器,有 next() 方法,执行返回 { value, done } 结构
const arr = [10, 20, 30];
const iterator = arr[Symbol.iterator]();

iterator.next(); // {value: 10, done: false}
iterator.next(); // {value: 20, done: false}
iterator.next(); // {value: 30, done: false}
iterator.next(); // {value: undefined, done: true}

另外,有些对象的 API 也会生成有序对象

const map = new Map([
  ["k1", "v1"],
  ["k2", "v2"],
]);
const mapIterator = map[Symbol.iterator]();

const values = map.values();
const valuesIterator = values[Symbol.iterator]();

自定义迭代器

interface IteratorRes {
  value: number | undefined;
  done: boolean;
}

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

  next(): IteratorRes {
    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());

函数:

// 定义生成器函数,入参是任意集合
function iteratorGenerator(list) {
  // idx记录当前访问的索引
  let idx = 0;
  // len记录传入集合的长度
  const len = list.length;
  return {
    // 自定义next方法
    next: function () {
      // 如果索引还没有超出集合长度,done为false
      const done = idx >= len;
      // 如果done为false,则可以继续取值
      const value = !done ? list[idx++] : undefined;

      // 将当前值与遍历是否完毕(done)返回
      return {
        done: done,
        value: value,
      };
    },
  };
}

const iterator = iteratorGenerator(["1号选手", "2号选手", "3号选手"]);
console.log(iterator.next()); // { done: false, value: '1号选手' }
console.log(iterator.next()); // { done: false, value: '2号选手' }
console.log(iterator.next()); // { done: false, value: '3号选手' }
console.log(iterator.next()); // { done: true, value: undefined }

有序结构的作用

for...of

  • 所有有序结构,都支持 for...of 语法

支持某些数组操作

  • 数组解构
const [node1, node2] = someDomList
  • 扩展操作符
const arr = [...someDomList]
  • Array.from()
const arr = Array.form(someDomList)
  • 创建 Map 和 Set
const map = new Map([
    ['k1', 'v1'],
    ['k2', 'v2']
])

const set = new Set(someDomList)
  • Promise.all 和 Promise.race
Promise.all([promise1, promise2, promise3])
Promise.race([promise1, promise2, promise3])

Generator

  • 迭代器和生成器,两者密不可分

基本使用

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

const numsIterator = genNums();
numsIterator.next(); // {value: 10, done: false}
numsIterator.next(); // {value: 20, done: false}
numsIterator.next(); // {value: 30, done: false}
numsIterator.next(); // {value: undefined, done: true}

// for (let n of numsIterator) {
//     console.log(n)
// }

yield* 语法

  • 有序结构可用于 yield*
function* genNums() {
  yield* [100, 200, 300]; // 相当于:循环数组,分别 yield
}
const numsIterator = genNums();
numsIterator.next(); // {value: 100, done: false}
numsIterator.next(); // {value: 200, done: false}
numsIterator.next(); // {value: 300, done: false}
numsIterator.next(); // {value: undefined, done: true}

// for (let n of numsIterator) {
//     console.log(n)
// }

最简单的自定义迭代器

class CustomIterator {
  private data: number[];

  constructor() {
    this.data = [10, 20, 30];
  }

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

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

yield 遍历 DOM 树

function* traverse(elemList: Element[]): any {
  for (const elem of elemList) {
    yield elem;

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

const container = document.getElementById("container");
if (container) {
  for (let node of traverse([container])) {
    console.log(node);
  }
}