上回说到如何让我们的自定义对象去实现可迭代接口,从而实现能够使用 for ... of 循环去迭代我们的对象,其实就是设计模式中的迭代器模式。通过下面这个案例可以了解一些迭代器设计模式的优势,假设我们有这样一个多人协同开发一个任务清单应用的需求,其中A 的任务就是去设计一个用于存放所有任务的对象 ,B 的任务就是被 A 定义这个对象当中所有的任务项全部去罗列呈现到界面上。A 为了更有结构的去记录每一个数据这里去设计一个对象结构 ,在这个对象中会定义两个数组分别用于去存放生活类 life 、学习类 learn 的任务 。 此时对于 B 而言就必须要了解 A 所定义的对象的数据结构是怎么样的,才能够有可能去遍历这个对象当中全部的数据内容,从而 B 的代码就有可能分别遍历对象中的两个数组去呈现所有的任务。假设 A 的结构发生了变化添加了一个全新的类目 work,因为B 的代码和 A 的代码存在严重的耦合,从而也要跟着一起去变化 。
// 迭代器设计模式
// 场景:多人协同开发一个任务清单应用
// A 的代码 -----------------------------------
const todos = {
life: ['吃饭', '睡觉', '打豆豆'],
learn: ['语文', '数学', '外语'],
work: ['喝茶']
}
// B 的代码 -----------------------------------
for (const item of todos.life) { ... }
for (const item of todos.learn) { ... }
for (const item of todos.work) { ... }
这样的代码扩展性会特别的差同时也增加了维护成本,但如果 A 的数据结构能够对外提供一个统一的遍历接口,那么对于 B 而言也就是对于调用者而言那就不用再去关系对象内部的数据结构,更不用担心 A 的内部结构改变过后所产生的影响。
// 迭代器设计模式
// 场景:多人协同开发一个任务清单应用
// A 的代码 -----------------------------------
const todos = {
life: ['吃饭', '睡觉', '打豆豆'],
learn: ['语文', '数学', '外语'],
work: ['喝茶'],
each: function(callback) {
const all = [].contact(this.life, this.learn, this.work)
for(const item of all) {
callback(item)
}
}
[Symbol.iterator]:function() {
const all = [...this.life, ...this.learn, ...this.work];
let index = 0;
return {
next: function() {
return {
value: all[index],
done: index++ >= all.length
}
}
}
}
}
// B 的代码 -----------------------------------
todos.each(function(item){
console.log(item)
})
for (const item of todos) {
console.log(item)
}
这就是我们实现迭代器的意义,迭代器这样一个模式它的核心就是对外提供统一遍历接口,让外部不用再去关心这个数据内部的结构是怎样的。只不过我们这里使用的 each 方法它只适用于我们当前这个对象结构,而 ES2015 的迭代器它是语言层面的去实现迭代器模式,所以说它可以去适用于任何数据结构只需要通过代码去实现一个 iterator 方法实现它的迭代逻辑就可以了,这种模式其实在很多地方都会用到只不过很多时候我们的观察都停留在表象上。认为我们知道某一个 API 的使用就可以,根本就不会关心它内部做的这样一些事情,或者说忽略了很多的为什么。