JavaScript设计模式之迭代器模式

517 阅读4分钟

本文由我们团队肖建朋总结

定义

迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。

迭代器模式属于行为型模式。

意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

主要解决:不同的方式来遍历整个整合对象。

何时使用:遍历一个聚合对象。

如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。

迭代器分为内部迭代器外部迭代器

内部迭代器

内部迭代器是指在迭代器内部已经定义了迭代规则,在使用时,外部只需一次初始调用,不用关心迭代器内部的实现。

下面我们来实现一个内部迭代器:

var inEach = function (ary, callback) {

  for(var i = 0; i< ary.length; i++) {
    callback(i, ary[i])
  }
}
inEach([1,2,3,4], function(i, item){
  console.log(i, item) // 
})

inEach函数对于外部来说,只需调用一次就可以输出数组的每一项。

由于内部迭代器迭代规则已经提前规定好了,如果我们不需要一次获取到数组所有的项,只需要一次获取一项或几项(例如我们经常遇到的分页问题)。那么内部迭代器显示不满足我们的需求。这里就需要用到外部迭代器。

### 外部迭代器

下面我们先通过一个简单的例子介绍如何使用JavaScript实现一个外部迭代器。

新建一个html文件,粘贴下面的代码保存html文件,用浏览器打开,F12打开开发者工具,点击下一个,会在Console中输入对应的值。

<!DOCTYPE html>
<html>
<body>
  <script>
    var each = function (ary, limit) {
      var i = 0
      return{
        next: function () {
          console.log(i)
          if( i + limit <= ary.length) {
            console.log(ary.slice(i, i + limit))
          } else if(i <= ary.length -1) {
            console.log(ary.slice(i, ary.length))
          } else {
            console.log('没有更多数据了')
          }
          i += limit
        }
      }
    }
    var e = new each([1,2,3,4,5,6,7,8,9,10], 3)
    function next() {
      e.next()
    }
    function reset() {
      e = new each([1,2,3,4,5,6,7,8,9,10])
    }
  </script>
  <p>
    JavaScript 迭代器示例:
  </p>
  <input type="button" value="下一个" onclick="next()">
  <input type="button" value="重置" onclick="reset()">
</body>

</html>
each函数就是一个迭代器,对外接收一个数组参数ary和步长limit(每次输出的数组项数),提供一个next的方法,当我们需要使用时new一个each对象 e , 每次我们只需调用e.next() 就可以得到数组下次内容。这里我们每次输出数组的3个项。log的内容如图:

其它几中迭代器

现实中还会遇到其它集中迭代器

  1. 倒序迭代器
  2. 终止迭代器

倒序迭代器

上面的内部迭代器是顺序循环数组的每一项,顾名思义,倒序迭代器就是从最后一项开始依次获取数组的每一项直到第一项。
var reverseEach = function (ary, callback) {
  for (var l = ary.length - 1; l >= 0; l--) {
    callback(l, ary[l]);
  }
}
reverseEach([0, 1, 2], function (i, n) {
  console.log(n); // 分别输出:2, 1 ,0
})

####终止迭代器

迭代器可以像for循环一样,提供一种跳出循环的方法。只需在迭代器内部约定一个条件,当满足该条件时就终止循环。
var each = function (ary, callback) {
  for (var i = 0, l = ary.length; i < l; i++) {
    if (callback(i, ary[i]) === false) { // callback 的执行结果返回false,提前终止迭代
      break;
    }
  }
}
each([1, 2, 3, 4, 5], function (i, n) {
  if (n > 3) { // n 大于3 的时候终止循环
    return false;
  }
  console.log(n); // 分别输出:1, 2, 3
})

总结

迭代器是一种相对简单的模式,目前绝大多数语言都内置了迭代器。

使用场景

  1. 访问一个聚合对象的内容而无须暴露它的内部表示。
  2. 需要为聚合对象提供多种遍历方式。
  3. 为遍历不同的聚合结构提供一个统一的接口。

推荐资料:

ES6中的迭代器和生成器

使用leanCloud实现迭代查询(这是我个人在实际项目使用迭代模式的实例总结)