javascript设计模式——迭代器模式
前面的话
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。迭代器模式是一种相对简单的模式,简单到很多时候都不认为它是一种设计模式。本文将详细介绍迭代器模式
迭代器实现
迭代器模式无非就是循环访问聚合对象中的各个元素。比如jQuery中的$.each函数,其中回调函数中的参数i为当前索引,n为当前元素,代码如下:
$.each( [, , ], function( i, n ){
console.log( '当前下标为: '+ i );
console.log( '当前值为:' + n );
});
现在来自己实现一个each函数,each函数接受2个参数,第一个为被循环的数组,第二个为循环中的每一步后将被触发的回调函数
var each = function( ary, callback ){
for ( var i = , l = ary.length; i < l; i++ ){
callback.call( ary[i], i, ary[ i ] ); // 把下标和元素当作参数传给callback 函数
}
}; each( [ , , ], function( i, n ){
alert ( [ i, n ] );
});
迭代器分类
迭代器可以分为内部迭代器和外部迭代器,它们有各自的适用场景
【内部迭代器】
刚刚编写的each函数属于内部迭代器,each函数的内部已经定义好了迭代规则,它完全接手整个迭代过程,外部只需要一次初始调用
内部迭代器在调用的时候非常方便,外界不用关心迭代器内部的实现,跟迭代器的交互也仅仅是一次初始调用,但这也刚好是内部迭代器的缺点。由于内部迭代器的迭代规则已经被提前规定,上面的each函数就无法同时迭代2个数组了
比如现在有个需求,要判断2个数组里元素的值是否完全相等,如果不改写each函数本身的代码,能够入手的地方似乎只剩下each的回调函数了,代码如下:
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( [ , , ], [ , , ] ); // throw new Error ( 'ary1 和ary2 不相等' );
【外部迭代器】
外部迭代器必须显式地请求迭代下一个元素。外部迭代器增加了一些调用的复杂度,但相对也增强了迭代器的灵活性,可以手工控制迭代的过程或者顺序
var Iterator = function( obj ){
var current = ;
var next = function(){
current += ;
};
var isDone = function(){
return current >= obj.length;
};
var getCurrItem = function(){
return obj[ current ];
};
return {
next: next,
isDone: isDone,
getCurrItem: getCurrItem
}
};
下面来改写compare函数:
var compare = function( iterator1, iterator2 ){
while( !iterator1.isDone() && !iterator2.isDone() ){
if ( iterator1.getCurrItem() !== iterator2.getCurrItem() ){
throw new Error ( 'iterator1 和iterator2 不相等' );
}
iterator1.next();
iterator2.next();
}
alert ( 'iterator1 和iterator2 相等' );
}
var iterator1 = Iterator( [ , , ] );
var iterator2 = Iterator( [ , , ] );
compare( iterator1, iterator2 ); // 输出:iterator1 和iterator2 相等
外部迭代器虽然调用方式相对复杂,但它的适用面更广,也能满足更多变的需求。内部迭代器和外部迭代器在实际生产中没有优劣之分,究竟使用哪个要根据需求场景而定
迭代类数组
迭代器模式不仅可以迭代数组,还可以迭代一些类数组的对象。比如arguments、{"0":'a',"1":'b'}等。无论是内部迭代器还是外部迭代器,只要被迭代的聚合对象拥有length属性而且可以用下标访问,那它就可以被迭代
在javascript中,for in语句可以用来迭代普通字面量对象的属性。jQuery中提供了$.each函数来封装各种迭代行为
$.each = function( obj, callback ) {
var value,
i = ,
length = obj.length,
isArray = isArraylike( obj );
if ( isArray ) { // 迭代类数组
for ( ; i < length; i++ ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) {
break;
}
}
} else {
for ( i in obj ) { // 迭代object 对象
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) {
break;
}
}
}
return obj;
};
倒序迭代器
迭代器模式提供了循环访问一个聚合对象中每个元素的方法,但它没有规定以顺序、倒序还是中序来循环遍历聚合对象
下面实现一个倒序访问的迭代器
var reverseEach = function( ary, callback ){
for ( var l = ary.length - ; l >= ; l-- ){
callback( l, ary[ l ] );
}
}; reverseEach( [ , , ], function( i, n ){
console.log( n ); // 分别输出:2, 1 ,0
});
中止迭代器
迭代器可以像普通for循环中的break一样,提供一种跳出循环的方法。在jQuery的each函数里有这样一句:
if(value===false){
break;
}
这句代码的意思是,约定如果回调函数的执行结果返回false,则提前终止循环。下面把之前的each函数改写一下:
var each = function( ary, callback ){
for ( var i = , l = ary.length; i < l; i++ ){
if ( callback( i, ary[ i ] ) === false ){ // callback 的执行结果返回false,提前终止迭代
break;
}
}
}; each( [ , , , , ], function( i, n ){
if ( n > ){ // n 大于3 的时候终止循环
return false;
}
console.log( n ); // 分别输出:1, 2, 3
});
文件上传
下面是一段关于文件上传的代码,目的是根据不同的浏览器获取相应的上传组件对象:
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') );
}
}
};
在不同的浏览器环境下,选择的上传方式是不一样的。因为使用浏览器的上传控件进行上传速度快,可以暂停和续传,所以首先会优先使用控件上传。如果浏览器没有安装上传控件,则使用Flash上传,如果连Flash也没安装,那就只好使用浏览器原生的表单上传了
上面的代码为了得到一个upload对象,getUploadObj函数里面充斥了try,catch以及if条件分支。缺点显而易见,很难阅读,且严重违反开闭原则。在开发和调试过程中,需要来回切换不同的上传方式,如果增加了一些另外的上传方式,比如,HTML5上传,这时唯一的办法是继续往getUploadObj函数里增加条件分支
目前一共有3种可能的上传方式,但不知道目前正在使用的浏览器支持哪几种。把每种获取upload对象的方法都封装在各自的函数里,然后使用一个迭代器,迭代获取这些upload对象,直到获取到一个可用的为止
var getActiveUploadObj = function(){
try{
return new ActiveXObject( "TXFTNActiveX.FTNUpload" ); // IE 上传控件
}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') );
};
在getActiveUploadObj、getFlashUploadObj、getFormUpladObj这3个函数中都有同一个约定:如果该函数里面的upload对象是可用的,则让函数返回该对象,反之返回false,提示迭代器继续往后面进行迭代
所以我们的迭代器只需进行下面这两步工作:1、提供一个可以被迭代的方法,使得getActiveUploadObj,getFlashUploadObj以及getFlashUploadObj依照优先级被循环迭代;2、如果正在被迭代的函数返回一个对象,则表示找到了正确的upload对象,反之如果该函数返回false,则让迭代器继续工作
迭代器代码如下:
var iteratorUploadObj = function(){
for ( var i = , fn; fn = arguments[ i++ ]; ){
var uploadObj = fn();
if ( uploadObj !== false ){
return uploadObj;
}
}
};
var uploadObj = iteratorUploadObj( getActiveUploadObj, getFlashUploadObj, getFormUpladObj );
重构代码之后,获取不同上传对象的方法被隔离在各自的函数里互不干扰,try、catch和if分支不再纠缠在一起,使得可以很方便地的维护和扩展代码。比如,给上传项目增加了Webkit控件上传和HTML5上传,要做的仅仅是下面一些工作
var getWebkitUploadObj=function(){
//具体代码略
}; var getHtml5UploadObj=function(){
//具体代码略
};
依照优先级把它们添加进迭代器:
var uploadObj=iteratorUploadObj(getActiveUploadObj,getWebkitUploadObj,getFlashUploadObj,getHtml5UploadObj,getFormUpladObj);
javascript设计模式——迭代器模式的更多相关文章
- JavaScript设计模式 - 迭代器模式
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. 迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺 ...
- javascript设计模式-迭代器模式(Iterator)
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 19. 星际争霸之php设计模式--迭代器模式
题记==============================================================================本php设计模式专辑来源于博客(jymo ...
- java设计模式——迭代器模式
一. 定义与类型 定义:提供一种方法,顺序访问一个集合对象中的各个元素,而又不暴露该对象的内部表示 类型:行为型. 二. 使用场景 (1) 访问一个集合对象的内容而无需暴露它的内部表示 (2) 为遍 ...
- js设计模式--迭代器模式
迭代器模式: 迭代器模式提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该方法中的内部表示.js中我们经常会封装一个each函数用来实现迭代器. 理解的意思:提供一个方法,去把对象的每一项按 ...
- [Head First设计模式]生活中学设计模式——迭代器模式
系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...
- javascript 设计模式-----策略模式
在<javascript设计模式>中,作者并没有向我们介绍策略模式,然而它却是一种在开发中十分常见的设计模式.最常见的就是当我们遇到一个复杂的表单验证的时候,常常需要编写一大段的if和el ...
- JAVA 设计模式 迭代器模式
用途 迭代器模式 (Iterator) 提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示. 迭代器模式是一种行为型模式. 结构
- 深入浅出设计模式——迭代器模式(Iterator Pattern)
模式动机 一个聚合对象,如一个列表(List)或者一个集合(Set),应该提供一种方法来让别人可以访问它的元素,而又不需要暴露它的内部结构.针对不同的需要,可能还要以不同的方式遍历整个聚合对象,但是我 ...
随机推荐
- uploadify 配置后,页面显示无效果
uploadify使用的是Flash版本 谷歌浏览器:默认没有开启Flash,进行如下图设置即可
- Linux中gcc和g++
今天在Linux上编写一个小的C++程序运行,需要用到iostream中cout,一开始创建一个hello.c的文件编写如下代码: #include<stdio.h> #include&l ...
- eclipse禁用svg文件Validation
1.打开window>preferences>validation找到xml validator 2.点击xml validator最右侧的按钮打开xml校验规则窗口,选中exclude ...
- 一场围绕着‘Deeping Learning’的高考
Deep Learning的基本思想和方法 实际生活中,人们为了解决一个问题,如对象的分类(对象可是是文档.图像等),首先必须做的事情是如何来表达一个对象,即必须抽取一些特征来表示一个对象,如文本的处 ...
- plsql远程连接虚拟机上的oracle
下载oracle instantclient_11_2和plsql 将oracle instantclient_11_2解压到任意目录 在此目录下新建network,在network目录下新建admi ...
- MySQLbase
/*多行注释*/-- 单行注释-- 创建用户: CREATE USER '用户名'[@'主机名'] IDENTIFIED BY '密码'-- 主机名可以为空,省略主机名表示默认权限为%, 所有主机都可 ...
- 《项目架构那点儿事》——快速构建Junit用例
[前 言]按照惯例,在实际项目中我往往会对自己编写的程序进行测试,当测试通过后才能将其用于实战中,当然,编写单元测试是不可避免的,可以直接清晰的检验出 我们程序的可靠性.可只执行性,从中发现问题从而得 ...
- 走进Spark生态圈:环境的安装与配置
什么是Spark? Apache Spark 是一种大规模数据处理的快速通用引擎,使用基于内存的处理方式,较与MapReduce而言,解决了其shuffle多次IO操作带来的效率低问题,从而达到快速的 ...
- c#中获取路径方法
要在c#中获取路径有好多方法,一般常用的有以下五种: //获取应用程序的当前工作目录. String path1 = System.IO.Directory.GetCurrentDirectory() ...
- 【原创】使用workstation安装Xenserver 6.5+cloudstack 4.10----本地存储模式
1. 背景: 近期由于项目和个人学习得需求,开始接触到Cloudstack,虽然云计算概念在大学刚毕业的时候就已经略有耳闻,但是由于工作原因,也一直没有了解,下班后想自己折腾下cloudstack,便 ...