前面的话

  迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。迭代器模式是一种相对简单的模式,简单到很多时候都不认为它是一种设计模式。本文将详细介绍迭代器模式

迭代器实现

  迭代器模式无非就是循环访问聚合对象中的各个元素。比如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设计模式——迭代器模式的更多相关文章

  1. JavaScript设计模式 - 迭代器模式

    迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. 迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺 ...

  2. javascript设计模式-迭代器模式(Iterator)

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. 19. 星际争霸之php设计模式--迭代器模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

  4. java设计模式——迭代器模式

    一. 定义与类型 定义:提供一种方法,顺序访问一个集合对象中的各个元素,而又不暴露该对象的内部表示 类型:行为型. 二. 使用场景 (1) 访问一个集合对象的内容而无需暴露它的内部表示 (2)  为遍 ...

  5. js设计模式--迭代器模式

    迭代器模式: 迭代器模式提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该方法中的内部表示.js中我们经常会封装一个each函数用来实现迭代器. 理解的意思:提供一个方法,去把对象的每一项按 ...

  6. [Head First设计模式]生活中学设计模式——迭代器模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...

  7. javascript 设计模式-----策略模式

    在<javascript设计模式>中,作者并没有向我们介绍策略模式,然而它却是一种在开发中十分常见的设计模式.最常见的就是当我们遇到一个复杂的表单验证的时候,常常需要编写一大段的if和el ...

  8. JAVA 设计模式 迭代器模式

    用途 迭代器模式 (Iterator) 提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示. 迭代器模式是一种行为型模式. 结构

  9. 深入浅出设计模式——迭代器模式(Iterator Pattern)

    模式动机 一个聚合对象,如一个列表(List)或者一个集合(Set),应该提供一种方法来让别人可以访问它的元素,而又不需要暴露它的内部结构.针对不同的需要,可能还要以不同的方式遍历整个聚合对象,但是我 ...

随机推荐

  1. js实现强大功能

    作者:知乎用户链接:https://www.zhihu.com/question/48187821/answer/110002647来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...

  2. 通过网络路径获取的图片 btye 图片流互转

    楼主有一个需要用户用的网站要上传图片,图片不保存到网站,而是要专门存放到一个图片服务器上面,于是需要通过byte的形式来传输 之前写的一个本地图片流转于byte互转 后来发现通过网络路径获取的图片这个 ...

  3. 面试时,当你有权提问时,别客气,这是个逆转的好机会(内容摘自Java Web轻量级开发面试教程)

    前些天,我在博客园里写了篇文章,如何在面试中介绍自己的项目经验,收获了2千多个点击,这无疑鼓舞了我继续分享的热情,今天我来分享另外一个面试中的甚至可以帮助大家逆转的技巧,本文来是从 java web轻 ...

  4. two.js之实现动画效果

    一.什么是two.js? Two.js 是面向现代 Web 浏览器的一个二维绘图 API.Two.js 可以用于多个场合:SVG,Canvas 和 WebGL,旨在使平面形状和动画的创建更方便,更简洁 ...

  5. Node.js 回调函数

    Node.js 回调函数 Node.js 异步编程的直接体现就是回调. 异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了. 回调函数在完成任务后就会被调用,Node 使用了大量的回调函数, ...

  6. 【机器学习实战】第13章 利用 PCA 来简化数据

    第13章 利用 PCA 来简化数据 降维技术 场景 我们正通过电视观看体育比赛,在电视的显示器上有一个球. 显示器大概包含了100万像素点,而球则可能是由较少的像素点组成,例如说一千个像素点. 人们实 ...

  7. Android开发之漫漫长途 Ⅱ——Activity的显示之Window和View(2)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  8. day3--深入学习命令总结

    1.查看命令帮助的几种方法 a.[命令] --help   适用于一般命令,非内置命令 b.man  [命令]     适用于一般命令,非内置命令 c.help  [命令]     适用于内置命令 d ...

  9. 解决webstorm启动索引文件卡死问题

    问题 当目录下的文件数量较大时,用webstorm打开会出现卡顿,甚至卡死现象,例如:node_modules目录 解决方案 不让webstorm索引该目录下的文件步骤:1.node_modules目 ...

  10. 初始MyBatis

    初始MyBatis 框架的概念: 框架是一个提供可重复的功用结构的半成品.它为我们构建新的应用程序提供了极大的便利,一方面提供了可以拿来就用的工具,更重要的是提供了可重用的设计.D 框架技术的优势: ...