迭代器

118 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情

迭代器描述

迭代器模式 (Iterator Pattern)用于顺序地访问聚合对象内部的元素,又无需知道对象内部结构。使用了迭代器之后,使用者不需要关心对象的内部构造,就可以按序访问其中的每个元素。

因为 JavaScript 已经内置了迭代器的实现,在某些个很老的语言中,使用者可能会为了实现迭代器而烦恼,但是在 JavaScript 中则完全不用担心。

前端代码的迭代器

1、JavaScript 原生支持

随着 JavaScriptECMAScript 的发展,出现了很多方便好用的API,比如 Array 上的 filterforEachreduceflat 等,还有 MapSetString 等数据结构,也提供了原生的迭代器支持。

var arr = [1, 'red', false, 3.14]
arr.forEach(function(item) {
    console.log(item)
})

2、jQuery 中的 $.each 遍历方法

var arr = ['hello', 'world', '!']
function callback(index, item) {
    console.log(index, item)
}
$.each(arr, callback)

3、ES6 迭代器

  • ES6 规定,默认的迭代器部署在对应数据结构的 Symbol.iterator 属性上,如果一个数据结构具有 Symbol.iterator 属性,就被视为可遍历的,就可以用 for...of 循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator 方法

  • for-of 循环可以使用的范围包括 ArraySetMap 结构、上文提到的类数组结构、Generator 对象,以及字符串

  • 通过 for-of 可以使用 Symbol.iterator 这个属性提供的迭代器可以遍历对应数据结构,如果对没有提供 Symbol.iterator 的目标使用 for-of 则会抛错:

var foo = { a: 1 }
for (var key of foo) {
    console.log(key)
}
// 这样写报错

所以我们需要自己给foo定义一个迭代器,让其支持for-of循环:

var foo = {
    a: 1,
    [Symbol.iterator]: function() {
        var valArr = [
            { value: 1, done: false },
            { value: 2, done: false },
            { value: 'other', done: false },
            { value: undefined, done: true }
        ]
        return {
            next: function() {
                return valArr.shift()
            }
        }
    }
}
for (var key of foo) {
    console.log(key)
}
// 输出: 1
// 输出: 2
// 输出: other

实现一个迭代器

迭代器模式解决了以下问题:

  • 提供一致的遍历各种数据结构的方式,而不用了解数据的内部结构
  • 提供遍历容器(集合)的能力而无需改变容器的接口

一个迭代器通常需要实现以下接口:

  • hasNext():判断迭代是否结束,返回Boolean
  • next():查找并返回下一个元素

为Javascript的数组实现一个迭代器可以这么写:

const item = [1, 'red', false, 3.14];
function Iterator(items) {
  this.items = items;
  this.index = 0;
}
Iterator.prototype = {
  hasNext: function () {
    return this.index < this.items.length;
  },
  next: function () {
    return this.items[this.index++];
  }
}
// 使用
const iterator = new Iterator(item);
while(iterator.hasNext()){
  console.log(iterator.next());
}
//输出:1, red, false, 3.14

ES6提供了更简单的迭代循环语法 for...of,使用该语法的前提是操作对象需要实现 可迭代协议(The iterable protocol),简单说就是该对象有个Key为 Symbol.iterator 的方法,该方法返回一个iterator对象。

比如我们实现一个 Range 类用于在某个数字区间进行迭代:

function Range(start, end) {
  return {
    [Symbol.iterator]: function () {
      return {
        next() {
          if (start < end) {
            return { value: start++, done: false };
          }
          return { done: true, value: end };
        }
      }
    }
  }
}
// 使用
for (num of Range(1, 5)) {
  console.log(num);
}
// 输出:1, 2, 3, 4