彻底理解Iterator模式

221 阅读2分钟

首先来看看别人对Iterator都是怎么理解的?

Iterator是这样一种机制。
它是一种接口,为各种不同的数据结构提供统一的访问机制。
任何数据结构只要部署Iterator接口,就可以完成遍历操作。
tips: 遍历操作就是可以依次处理该数据结构的所有成员。 ---阮一峰ES6入门


在js中,Iterator是一个对象,它定义一个序列,并在终止时可能返回一个返回值。 具体地说,迭代器通过使用next()方法实现Iterator protocol的任何一个对象。这个对象长这个样子{value: '任意的值', done: 'true或false'}


我的理解

Iterator是这样一种机制:

  1. 顺序访问一个集合
  2. 使用者无需知道集合的内部结构(封装)

举个例子:

/*
    针对一个仅有3个<a>target</a>标签的页面来说
*/
var arr = [1, 2, 3];
var nodeList = document.getElementsByTagName('p');
var $p = $('a')

// 需要对这3种数据结构进行遍历
arr.forEach(function (item) {console.log(item)})

for (let i = 0,len = nodeList.length; i < len; i++) {console.log(nodeList(i))}

$a.each(function (key, p) {console.log(key, p)})

要对这3种数据结构进行遍历,所使用的编程方式是不一样的。
但是这3种数据结构是什么样我们是提前知道的。

现在提出一个问题,能否有这样一种函数,能对这三种数据结构进行统一的遍历处理。
其实jquery已经帮我们实现一种方法。

//假设已经引入了jquery
function each(data) {
    var $data = $(data); //[生成迭代器] 这个$(data)对象就是某种意义上的迭代器
    $data.each(function (key, val) {
        console.log(key, val)
    })
}

$(data)这个对象的好处是:

  1. 它可以顺序遍历有序集合
  2. 使用者不必知道集合内部结构

UML类图

// 迭代器生成
class Iterator {
    constructor(container) {
        this.list = container.list
        this.index = 0
    }
    
    next() {
        if (this.hasNext()) {
            return {value: this.list[this.index++], done: false}
        }
        return {done: true}
    }
    
    hasNext() {
        if (this.index >= this.list.length) {
            return false
        }
        return true
    }
}

class Container {
    constructor(list) {
        this.list = list
    }
    
    //生成迭代器
    getIterator() {
        return new Iterator(this)
    }
}

// 目标对象
let arr = [1, 2, 3, 4, 5, 6];
// 迭代器模式
let container = new Container(arr); 
//生成迭代器对象
let iterator = container.getIterator(); 
while (iterator.hasNext()) {
    console.log(iterator.next())
}

场景

  1. jQuery each
  2. ES6 Iterator

ES6 Iterator 为何存在?

  • ES6 语法中,有序集合的数据类型已经很多
  • Array Map Set String TypedArray arguments NodeList
  • 需要有一个统一的遍历接口来遍历所有数据类型
  • 以上数据类型,都有[Symbol.iterator]属性
  • 属性值是函数,执行函数返回一个迭代器
  • 这个迭代器就有next方法可顺序迭代子元素
// ES6 Iterator示例
function each(data, callback) {
    //生成迭代器
    let iterator = data[Symbol.iterator]();
    
    let item = {done: false};
    while (!item.done) {
        item = iterator.next();
        if (!item.done) {
            callback(item.value);
        }
    }
}

//ES6 实现
function each(data, callback) {
    for (let item of data) {
        callback(item)
    }
}

Iterator 和 Generator

  • Iterator的价值不限于上述几个类型的遍历
  • 还有Generator函数的使用,Generator是生成器,即生成一个迭代器
  • 即只要返回的数据格式符合Iterator接口的要求
  • 即可使用Iterator语法,这就是迭代器模式
funciton* helloworldGenerator() {
    yield 'hello';
    yield 'world';
    return 'ending';
}
var hw = helloworldGenerator();
hw[Symbol.iterator] //f [Symbol.iterator]() { [native code] }
//可以看到,Generator函数返回的结果,也实现了 Iterator 接口,即返回了一个迭代器

设计原则验证

  • 迭代器对象和目标对象分离
  • 迭代器将使用者与目标对象隔离开
  • 符合开放封闭原则