前言:要提升代码水平,就绕不开设计模式。之前也有过一些了解,但并没有深入学习。最近准备系统的学习一下设计模式,提高设计,解耦的能力,发现了一本好书《JavaScript设计模式与开发实践》,所以边读边写,把常用的设计模式学习并记录在这里。
定义与介绍
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。
迭代器模式是一种相对简单的设计模式,简单到很多时候我们都不认为它是一种设计模式,目前绝大部分语言都内置了迭代器。
现代语言中都内置了迭代器,比如js中的Array.prototype.forEach
。我们也可以自己实现一个迭代器:
const each = (arr, cb) {
for(let i=0; i<arr.length; i++){
cb.call(arr[i], i, arr[i]);
}
}
内部迭代器 和 外部迭代器
我们在js中常用的forEach
就是内部迭代器,在其内部已经定义好了迭代规则,他完全接手整个迭代过程,外部只需要初始调用一次即可。
而外部迭代器必须显式的请求迭代下一个元素。比如:
const Iterator = function(arr) {
const current = 0;
const next = function(){
current += 1;
}
const isDone = function(){
return current >= arr.length;
}
const getCurrentItem = function(){
return arr[current];
}
return {
next,
isDone,
getCurrentItem,
}
}
// 使用时,通过
Iterator.isDone() //判断是否迭代完成
Iterator.next() //显式调用进行一次迭代
Iterator.getCurrentItem() //获取当前项
JavaScript中的迭代器模式
Array.prototype.forEach
迭代器模式的例子
假设之前的代码中,有这样一个需求:上传文件时根据不同的浏览器来获取相应的上传组件对象。
// 原来的代码
const getUploadObj = function() {
try{
return new ActiveXObject('TXFTNActive.FTNUpload'); // IE上传控件
} cache(e) {
if(supportFlash()){ // 是否支持flash
const str = '<object type="applocation/x-shockwave-flash"></object>';
return $(str).appendTo($('body'));
} else {
const str = '<input name="file" type="file" />'; // 表单上传
return $(str).appendTo($('body'));
}
}
}
这样的代码明显很乱,而且各种上传方法之间没有解耦,如果要新增其他的,还要增加if else
分支,违反开放封闭原则。
下面我们换个思路,我们有多种上传方式,只需要按优先级迭代每一种方式,如果适用则返回对象,否则返回false,直到获取一个可用的为止。当然,这要求我们对于每一种方式,都要在可用时返回对象,不可用时返回false。保证函数的输入输出一致。
代码:
// IE上传控件
const getActiveUpoladObj = function() {
try{
return new ActiveXObject('TXFTNActive.FTNUpload');
} cache(e) {
return false;
}
}
// flash上传
const getFlashUpoladObj = function() {
const str = '<object type="applocation/x-shockwave-flash"></object>';
return $(str).appendTo($('body'));
}
// 表单上传
const getFormUpoladObj = function() {
const str = '<input name="file" type="file" />';
return $(str).appendTo($('body'));
}
// 迭代器
const iteratorUploadObj = function() {
for(let i=0; i<arguments.length; i++) {
let uploadObj = fn();
if(uploadObj !== false) {
return uploadObj;
}
}
}
// 使用
const uploadObj = iteratorUploadObj(getActiveUpoladObj, getFlashUpoladObj, getFormUpoladObj);