js设计模式–迭代器模式

40次阅读

共计 3020 个字符,预计需要花费 8 分钟才能阅读完成。

前言
本系列文章主要根据《JavaScript 设计模式与开发实践》整理而来,其中会加入了一些自己的思考。希望对大家有所帮助。
文章系列
js 设计模式 – 单例模式
js 设计模式 – 策略模式
js 设计模式 – 代理模式
概念
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。
UML 类图

场景
JavaScript 已经内置迭代器,如 forEach Iterator 等,再如 jquery 的 $.each
分类
内部迭代器
定义
内部已经定义好了迭代规则,它完全接手整个迭代过程,外部只需要一次初始调用

实现
var each = function (ary, callback) {
for (var i = 0; i < ary.length; i++) {
callback(i, ary[i])
}
}
each([1, 2, 3, 4, 5], function (i, item) {
console.log(i, item)
})
优缺点
优点:内部迭代器在调用的时候非常方便,外界不用关心迭代器内部的实现,跟迭代器的交互也仅 仅是一次初始调用
缺点:由于内部迭代器的迭代规则已经被提前规 定,上面的 each 函数就无法同时迭代 2 个数组,如下代码

var compare = function(ary1, ary2){
if (ary1.length !== ary2.length){
throw new Error (‘ary1 和 ary2 不相等 ’);
}
each(ary1, function( i, n){
if (n !== ary2[ i] ){
throw new Error (‘ary1 和 ary2 不相等 ’);
}
});
alert (‘ary1 和 ary2 相等 ’);
};
compare([ 1, 2, 3], [1, 2, 4] ); // throw new Error (‘ary1 和 ary2 不相等 ’);
外部迭代器
定义
外部迭代器必须显式地请求迭代下一个元素

实现
我们模拟一个 es6 迭代器
var Iterator = function (ary) {
this.ary = ary
this.index = 0
}

Iterator.prototype.isDone = function () {
return this.index >= this.ary.length
}

Iterator.prototype.next = function () {
if (!this.isDone()) {
var res = this.ary[this.index]
this.index++
return {
value: res,
done: this.isDone()
}
}
}

var a = new Iterator([1, 2, 3])

while (!a.isDone()) {
console.log(a.next())
}
下面解决一下上面那个问题

var a = new Iterator([1, 2, 3, 3])
var b = new Iterator([1, 2, 3])

function compare(iterator1, iterator2) {
while (!iterator1.isDone() || !iterator2.isDone()) {
if (iterator1.next().value !== iterator2.next().value) {
return false
}
}
return true
}

compare(a, b)
例子
文件上传
实现文件上传对象

var getUploadObj = function () {
try {
return new ActiveXObject(“TXFTNActiveX.FTNUpload”);
} catch (e) {
// IE 上传控件
if (supportFlash()) {// supportFlash 函数未提供
var str = ‘<object type=”application/x-shockwave-flash”></object>’;
return $(str).appendTo($(‘body’));
} else {
var str = ‘<input name=”file” type=”file”/>’; // 表单上传
return $(str).appendTo($(‘body’));
}
}
};
缺点:第一是很难阅读,第二是严重违反开闭原则
改进

var getActiveUploadObj = function () {
try {
return new ActiveXObject(“TXFTNActiveX.FTNUpload”);
} catch (e) {
return false;
}
};

var getFlashUploadObj = function () {
if (supportFlash()) {// supportFlash 函数未提供
var str = ‘<object type=”application/x-shockwave-flash”></object>’;
return $(str).appendTo($(‘body’));
};
return false;
}

var getFormUpladObj = function () {
var str = ‘<input name=”file” type = “file” class = “ui-file” / > ‘; // 表单上传
return $(str).appendTo($(‘body’));
}

var iteratorUploadObj = function () {
for (var i = 0, fn; fn = arguments[i++];) {
var uploadObj = fn();
if (uploadObj !== false) {
return uploadObj;
}
};
}
var uploadObj = iteratorUploadObj(getActiveUploadObj, getFlashUploadObj, getFormUpladObj);
es6
基于类实现
class Iterator {
constructor(conatiner) {
this.list = conatiner.list
this.index = 0
}
next() {
if (this.hasNext()) {
return this.list[this.index++]
}
return null
}
hasNext() {
if (this.index >= this.list.length) {
return false
}
return true
}
}

class Container {
constructor(list) {
this.list = list
}
getIterator() {
return new Iterator(this)
}
}

// 测试代码
let container = new Container([1, 2, 3, 4, 5])
let iterator = container.getIterator()
while(iterator.hasNext()) {
console.log(iterator.next())
}

es6 中的 Iterator
我们都知道 Array、Map、Set、类对象(如 arguments NodeList 等)都有一个 Symbol.iterator 迭代方法,可以通过以下方式取得

var a = [1,2,3]
console.log(a[Symbol.iterator])
另外 generator 也会返回迭代器
function* gen() {
yield 1
yield ‘1’
}

var a = gen()
a.next()

正文完
 0