内部迭代器和外部迭代器
简单地讲,内部迭代器就是类似 forEach
的迭代器,其内部迭代逻辑已经制定好,只要传入一个可迭代的对象,就会一直迭代。外部迭代器是类似 ES 6 中的 iterator
,每一步的迭代都要手动进行。
迭代器应用举例
作者介绍了一种场景:有一个项目的上传模块,需要先判断浏览器有什么上传条件,根据优先级选出适合的上传模式,原来的代码是这样的:
var getUploadObj = function() {
try {
return new ActiveXObject("TXFTNActiveX.FTNUpload"); // IE 上传控件
} catch (e) {
if (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'));
}
}
}
作者是一个 if...else 强迫症,看到一个函数里有这么多 if...else ,还有 try...catch 语句,他觉得不行,我也觉得这样不好。不仅有很多条件判断语句,也违反 开放-封闭 原则,以后如果增加了其他的上传方式,如 HTML 5 上传,那就要再次改写这个函数。
这个时候就可以使用迭代器模式,将每种判断的方法抽出来变成一个个方法,然后将各个方法作为函数的参数参入另一个函数中,在函数中按优先级迭代出合适的方法出来即可:
var getActiveUploadObj = function() {
try {
return new ActiveXObject("TXFTNActiveX.FTNUpload"); // IE 上传控件
} catch (e) {
return false;
}
};
var getFlashUploadObj = function() {
if(supportFlash()) {
var str = '<object type="application/x-shockwave-flash"></object>';
return $('str').appendTo($('body'));
}
return false;
}
var getFormUploadObj = function() {
var str = '<input name="file" type="file" />';
return $('str').appendTo($('body'));
}
这三个函数都遵循了一个规则:如果该函数下的 Upload 对象是可用的,就返回该对象,反之则返回 false
,让迭代继续往后面迭代。
接着,我们再实现迭代这三个函数的函数:
var iteratorUploadObj = function() {
for (var i = 0, fn; fn = arguments[i++];) {
var uploadObj = fn();
if (uploadObj !== false) {
return uploadObj;
}
}
}
var uploadObj = iteratorUploadObj(getActiveUploadObj, getFlashUploadObj, getFormUploadObj);
这样写后,方法可以按优先级进行迭代,如果要添加新的上传方法,只需要往 iteratorUploadObj
传入新的参数即可,不需要修改已经写好的代码。简直棒棒。
与策略模式的对比
在前面的策略模式中,使用策略模式的痛点也在于一个函数里的 if...else 条件语句过多,那么什么时候用策略模式,什么时候用迭代器模式呢?
通过观察策略模式的使用,可以发现,策略模式的策略是被“显式调用”的,如前面的计算奖金场景下,先写好一个策略对象,里面是不同绩效下的不同策略,然后再写一个函数,用于将绩效等级传入,而绩效等级的名称刚好对应上了策略对象里不同策略的属性名。是一个“显式调用的过程”。
再比如缓动动画的案例里,将各种缓动函数写在了一个对象里,需要用到什么缓动,就调用哪个方法。
而迭代器模式是将方法一个个传入,我们并不知道哪个方法会生效。属于一股脑传进去,我们对选择哪个方法做不了主,所以才用到了迭代器。
所以以后如果有想要规避条件语句的情景,可以看看调用的方法我们是不是已经明确,明确了可以使用策略模式,不明确可以使用迭代器模式。
关于 arguments 对象的使用
在代理模式和策略模式中,发现很多代码用到了 arguments
对象和 apply
方法,但是我在实际开发中很少用到这两个。而不使用 arguments
的原因,大概是对这个对象有点“抵触”的情绪,因为在当时阅读《Javascript 高级程序设计》的过程中,看到书中说在严格模式下,arguments
的callee
和caller
方法是不允许使用的,且arguments
对象不允许修改(删除,添加元素),这让我感觉到使用arguments
是一件不好的事情,但是实际上,虽然不能修改arguments
对象,但是读取和遍历arguments
对象是允许的,而且可以让一些操作变得简便,比如策略模式中的遍历就用到了。所以以后在一些场景下可以考虑用用 arguments
。
网友评论