设计模式 - js 迭代器模式

192 阅读3分钟

「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战

在学习中有这样一段话:在学一样东西的时候,可以按照它是什么,有什么用,优点是什么,缺点是什么,要怎么去使用,或者使用的时候要注意什么。我觉得这五部曲还是很有用的,能帮你快速掌握一个知识点

迭代器模式是什么?

迭代器模式的定义:

迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。

在我们生活中就有着最常见的迭代器模式的例子,比方说我们去坐公交的时候,大家都是统一的刷卡或者投币,并不会去关心你是男生还是女生,它只关心你有没有投币。

我们经常使用的 js 中的 forEach 本质上也是一种迭代器模式。

而且迭代器模式又分为内部迭代器和外部迭代器,接下去将会分别介绍一下两个迭代器的作用和不同。

迭代器模式有什么用?

使得我们能按照顺序的方法访问一个聚合对象的各个元素,又不需要暴露对象内部的方法

不使用迭代器模式的话,我们如果要去访问一个聚合对象的各个元素,最常用的做法就是将对象的创建和遍历都放在一起,但是这种方法不利于程序的扩展,违背了 '开闭原则' 。

迭代器模式的优点?

  1. 访问一个聚合对象的内容而无须暴露它的内部表示。

  2. 耦合。遍历任务交由迭代器完成。

  3. 可扩展。可以按照自己的需求新增不同的遍历方法

迭代器模式的缺点?

  1. 由于迭代器模式将存储数据和遍历数据的职责分离,这在一定程度上增加了系统的复杂性

迭代器模式的使用

内部迭代器

内部迭代器的内部已经定义好了迭代的顺序和规则,完全由它来执行迭代的过程,外部只需要去调用初始化即可。

var each = function (ary, callback) {
  for (var i = 0, l = ary.length; i < l; i++) {
    callback(i, ary[i]); 
  }
};

let arr = ["a", "b", "c"];

each(arr, function (i, n) {
  console.log([i, n]);
});

以上就是一个简单的内部迭代器,主要就是实现了迭代的职责分离。

外部迭代器

外部迭代器和内部迭代器稍有不同,外部迭代器内部需要实现三个方法:

  1. 访问下一个元素方法 next()

  2. 当前遍历是否结束 isDone()

  3. 获取当前元素 getCurrentItem()

const iterator = function (obj) {
    let current = 0;

    const next = () => (current += 1);

    const isDone = () => current >= obj.length;

    const getCurrItem = () => {
    return obj[current];
    };

    const getCurrIndex = () => {
    return current;
    };

    return {
    next,
    isDone,
    getCurrItem,
    getCurrIndex,
    length: obj.length,
    };
};

let container = iterator(["a", "b", "c"]);

console.log(container.length);
while (!container.isDone()) {
    console.log(container.getCurrItem());
    console.log(container.getCurrIndex());
    container.next();
}


输出:
3
a 0 
b 1
c 2

我们也可以稍微的扩展外部迭代器的方法,增加我们想要的迭代内容,接下去用es6的class实现一下迭代器和生成迭代器的生成类:

class Iterator {
  //遍历器
  constructor(container) {
    this.list = container.list;
    this.current = 0;
    this.length = container.list.length;
  }
  next = () => (this.current += 1);
  isDone = () => this.current >= this.list.length;
  getCurrItem = () => {
    return this.list[this.current];
  };
  getCurrIndex = () => {
    return this.current;
  };
}

class Container {
  //容器
  constructor(list) {
    this.list = list;
  }
  //生成遍历器
  getIterator() {
    return new Iterator(this);
  }
}

let container = new Container(["a", "b", "c"]);

let inerator = container.getIterator();

console.log(inerator.length);

while (!inerator.isDone()) {
  console.log(inerator.getCurrItem(), inerator.getCurrIndex());
  inerator.next();
}

输出:
3
a 0 
b 1
c 2

这样我们就可以根据扩展我们的迭代器类,然后根据容器生成不同的迭代器实例,来实现迭代器的自定义。

总结

迭代器模式是一种相对简单的设计模式,我们日常中使用的最多的就是迭代器模式,并且现在的大多数语言都是有内置的迭代器的,所以很少需要我们自己去定义迭代器,除非一些比较特殊的遍历情况,大部分情况下我们还是使用现有的语言提供的迭代器就可以了。