1 迭代器模式的定义
迭代器模式是指提供一种方法顺序访问
一个聚合对象
中的各个元素,而又不需要暴露该对象的内部表示。 迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。
2 jquery中的迭代器
迭代器模式无非就是循环访问局和对象中的每个元素。例如jquery中的$.earch函数。其中回调函数中的参数i为当前索引,n为元素。
$.each([1, 2, 3], function(i, n) {console.log(`当前下标:${i}`);console.log(`当前值为:${n}`);
})
复制代码
3 实现自己的迭代器
我们来实现一个拓展数组的eachPlus函数,实现each函数相同的遍历功能。参数是一个回调函数
Array.prototype.eachPlus = function(callback) {var self = this;for(var i = 0; i < self.length; i++) {callback(self[i], i, self);}
}
var arr = [1, 2, 3];
arr.eachPlus(function(value, index) {console.log(value, index);
})
复制代码
4 内部迭代器与外部迭代器
- 内部迭代器:函数内部已经定义好迭代规则, 它完全接手整个迭代过程,外部只需要一次初始化调用。
- 外部迭代器:必须显示请求迭代下一个元素。 由于内部迭代器已经将迭代过程定义好了,因此我们想要在规则中修改代码是不可能了。假如我们想要比较两个数组是否相等,只能在回调函数中进行着手:
function compare(arr1, arr2) {arr1.eachPlus(function(value, index) {if (arr1.length !== arr2.length) {throw new Error('arr1 != arr2');}if (arr2[index] !== value) {throw new Error('arr1 != arr2');}console.log('arr1 == arr2');});
}var arr1 = [1, 2, 3];
var arr2 = [1, 2, 3];
compare(arr1, arr2);
复制代码
外部迭代器增加了一些调用的复杂度,但是相对增强了迭代器的灵活性,我们可以手工控制迭代的过程或者顺序。下面是一个外部迭代器的例子
Array.prototype.iterator = function() {var self = this;var index = 0;// 获取当前元素var current = function() {return self[index];};// 获取当前元素,并移动索引到下一个位置var next = function() {var el ;if (!hasNext()) {return null;}el = current();index++;return el;};// 是否还有元素var hasNext = function() {return index < self.length ? true : false;};// 重置迭代器var rewind = function() {index = 0;}; return {next: next,hasNext: hasNext,current: current,rewind: rewind}
}// 调用
function compare(arr1, arr2) {var iter1 = arr1.iterator();var iter2 = arr2.iterator();while (iter1.hasNext() || iter2.hasNext()) {if (iter1.current() !== iter2.current()) {throw new Error('不相等');}iter1.next();iter2.next();}console.log('相等');
}
compare([1,2,3,4], [1,2,3,4])
复制代码
5 迭代器模式应用
当上传一个文件,我们有一个上传的控件。但是不同的浏览器兼容问题,应该使用不一样的控件。
- 获取IE控件
- 不存在则使用flash控件
- 以上都不存在使用原生表单上传
var getUploadObj = function(){try{return new ActiveXObject("TXFTNActiveX.FTNUpload"); // IE 上传控件} catch(e) {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') );}}
};
复制代码
上面的代码中,为了得到一个upload控件,使用了很多if,else,并且使用了try catch。这个例子就像是我们有1扇门,手里有三把钥匙,不知道拿一把可以打开,那么就只有一直尝试,直到正确为止。下面我们尝试使用内部迭代器模式
解决这个问题:
- 将上传控件各自封装
- 组装空间数组
- 进行迭代获取正确钥匙
var IEUpload = function() {try{return new ActiveXObject("TXFTNActiveX.FTNUpload"); // IE 上传控件} catch(e) {return false;}
}var flashUpload = function() {if (supportFlash()) { // supportFlash 函数未提供var str = '<object type="application/x-shockwave-flash"></object>';return $( str ).appendTo( $('body') );}return false;
}var getFormUpload = function() {var str = '<input name="file" type="file"/>'; // 表单上传return $( str ).appendTo( $('body') );
}// 编写内部迭代器模式
function getUpload() {for(var i = 0, fn; fn = arguments[i++];) {var uploadObj = fn();if (uploadObj) {return uploadObj;}}
}// 使用内部迭代器
getUpload(IEUpload, flashUpload, getFormUpload);
复制代码
前面提到:将不变的部分
和变化的部分
隔开是每个设计模式的主题。这里我们也可以看出来,getUpload就是分离出来的不可变的部分
。当我们需要增加一个HTML5的上传控件类型时,不再需要修改整个代码,只需要添加一个上传规则html5Upload()函数
在进行调用即可。
6 小结
迭代器模式比较简单,很多时候许多人都认为它不是一种设计模式。目前绝大部分语言都内置了迭代器。