迭代器模式

概念介绍

迭代器模式(Iterator): 在不暴露对象内部结构的同时,可以顺序地访问聚合对象内部的元素。

迭代器

程序中的循环是一种利器,循环语句也使我们程序开发更简洁高效,但是有时一遍又一遍的重复性循环却让代码显得循环不堪。

这个时候我们就可以用迭代器来简化我们的循环遍历操作,下面我们创建一个迭代器类。

//迭代器
var Iterator=function(items,container){
//获取父容器,若container参数存在,并且可以获取该元素则获取,否则获取document
var container=container&&document.getElementById(container)||document,
//获取元素
items=container.getElementsByTagName(items),
//获取元素长度
length=items.length,
//当前索引值
index=0;
//缓存原生数组splice方法
var splice=[].splice;
return{
//获取第一个元素
first:function(){
index=0;
return items[index];
},
//获取最后一个元素
second:function(){
index=length-1;
return items[index];
},
//获取前一个元素
pre:function(){
if(--index>0){
return items[index];
}else{
index=0;
return null;
}
},
//获取后一个元素
next:function(){
if(++index<length){
return items[index];
}else{
index=length-1;
return null;
}
},
//对每一个元素执行某一个方法
get:function(num){
//如果num大于等于0再获取正向获取,否则逆向获取
index=num>=0?num%length:num%length+length;
console.log(index);
return items[index];
},
//对某一个元素执行某一个方法
dealEach:function(fn){
//第二个参数开始为回调函数中参数
var args=splice.call(arguments,1);
for (var i=0;i<length;i++) {
fn.apply(items[i],args);
}
},
//排他方式处理某一个元素
dealItem:function(num,fn){
//第三个参数开始为回调函数中参数
//用this.get方法设置index索引值
fn.apply(this.get(num),splice.call(arguments,2))
},
exclusive:function(num,allFn,numFn){
//对所有元素执行回调函数
this.dealEach(allFn);
//如果num类型为数组
if(Object.prototype.toString.call(num)==="[object Array]"){
//遍历num数组
for(var i=0;i<num.length;i++){
this.dealItem(num[i],numFn);
}
}else{
//处理第num个元素
this.dealItem(num,numFn);
}
}
}
}

页面元素如下:

<ul id="container">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>

好了我们的迭代器就完成了,我们试着调用一下

var test=new Iterator("li","container");
//我们先试试获取元素
console.log(test.first());
console.log(test.pre());
console.log(test.next());
console.log(test.get(2000));

我们再来处理所有元素

test.dealEach(function(text,color){
this.innerHTML=text;
this.style.background=color;
},'背景变色','#dddddd');

接着我们选中前两个元素

test.exclusive([0,1],function(){
this.innerHTML='被排除的';
this.style.background='green';
},function(){
this.innerHTML='选中的';
this.style.background='red';
})

通过迭代器提供的接口方法就能轻易地访问聚合对象中的每一个元素。甚至不需要知道聚合对象的内部的具体结构。

数组迭代器

我们知道在低版本的IE下是不支持each方法的,那么我们就可以通过迭代器的思想定义一个数组迭代器来完成each操作

var eachArray=function(arr,fn){
for (var i=0;i<arr.length;i++) {
//依次执行回调函数,注意回调函数中传入的参数第一个为索引,第二个为索引对应的值
if(fn.call(arr[i],i,arr[i])===false){
break;
}
}
}

我们来测试一下

for(var arr=[],i=0;i<5;arr[i++]=i);
eachArray(arr,function(i,data){
console.log(i,data);
})

对象迭代器

对象迭代器与数组迭代器比较类似,但传入回调函数中的为对象的属性与对象的属性值

var eachObject=function(obj,fn){
for(var i in obj){
if(fn.call(obj[i],i,obj[i])===false){
break;
}
}
}

我们再来看看

var obj={a:1,b:2,c:3}
eachObject(obj,function(i,data){
console.log(i,data);
});

解决分支循环嵌套

当然我们也能用迭代器解决分支循环嵌套,比如我们使用canvas来处理图片像素绘制如下效果

页面元素如下:

<canvas id="canvas"></canvas>
<img src="img/test.jpg"/>
window.onload=function(){
//获取画布
var canvas=document.getElementsByTagName('canvas')[0],
//获取图片
img=document.images[0],
//获取并设置宽度
width=(canvas.width=img.width*2)/2,
//获取并设置高度
height=canvas.height=img.height,
//获取渲染上下文
ctx=canvas.getContext('2d');
//绘制图片
function dealImage(t,x,y,w,h,a){
//获取画布图片数据
var canvasData=ctx.getImageData(x,y,w,h);
//获取像素数据
var data=canvasData.data;
//遍历每组像素数据(4个数据表示一个像素点数据,分别代表红色、绿色、蓝色、透明度)
for (var i=0;i<data.length;i+=4) {
switch(t){
case 'red':
data[i+1]=0;
data[i+2]=0;
data[i+3]=a;
break;
case 'green' :
data[i]=0;
data[i+2]=0;
data[i+3]=a;
break;
case 'blue':
data[i]=0;
data[i+1]=0;
data[i+3]=a;
case 'gray':
var num=parseInt((data[i]+data[i+1]+data[i+2])/3);
data[i]=num;
data[i+1]=num;
data[i+3]=a;
break;
}
//绘制处理后的图片
ctx.putImageData(canvasData,width+x,y); }
}
//为图片添加特效
dealImage('gray',0,0,width,height,255); }

然而我们发现,这种逻辑并不是最优的,因为每一次遍历都要进行一次分支判断,假如有一张比较大的图片会产生很多不必要的消耗,但是我们可以将循环遍历抽象出来作为一个迭代器存在,每次循环都执行传入迭代器中的某一固定算法,对于特效算法我们可以设置在策略对象中实现,通过策略模式与迭代器模式的综合运用就可以解决分支判断问题

function dealImage(t,x,y,w,h,a){
var canvasData=ctx.getImageData(x,y,w,h),
data=canvasData.data;
//策略模式封装算法
var Deal=function(){
var method={
'default':function(i){
return method['gray'](i);
},
'red':function(i){
data[i+1]=0;
data[i+2]=0;
data[i+3]=a;
},
'green':function(i){
data[i]=0;
data[i+2]=0;
data[i+3]=a;
},
'gray':function(i){
data[i]=data[i+1]=parseInt(data[i+2]=(data[i]+data[i+1]+data[i+2])/3);
data[i+3]=a;
}
};
//主函数,通过给定类型返回对应滤镜算法
return function(type){
return method[type]||method['default'];
}
}();
//迭代器处理数据
function eachData(fn){
for (var i=0;i<data.length;i+=4) {
fn(i);
}
}
eachData(Deal(t));
ctx.putImageData(canvasData,width+x,y);
}

总结

通过迭代器我们可以顺序地访问一个聚合对象中的每一个元素。在开发中,迭代器极大简化了代码中的循环语句,使代码结构清晰紧凑,然而这些简化了的循环语句实质上隐形地移到了迭代器中。当然用迭代器去处理一个对象时,我们只需提供处理的方法,而不必去关心对象的内部结构,这也解决了对象的使用者与对象内部结构之间的耦合。当然迭代器的存在也为我们提供了操作对象的一个统一接口

也谢谢大家看到这里:)如果你觉得我的分享还可以请点击推荐,分享给你的朋友让我们一起进步~

好了以上就是本次分享的全部内容,本次示例参考自JavaScript设计模式一书,让我们一点点积累一点点成长,希望对大家有所帮助。

欢迎转载,转载请注明作者,原文出处。

再起航,我的学习笔记之JavaScript设计模式25(迭代器模式)的更多相关文章

  1. 再起航,我的学习笔记之JavaScript设计模式08(建造者模式)

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...

  2. 再起航,我的学习笔记之JavaScript设计模式09(原型模式)

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 我们 ...

  3. 再起航,我的学习笔记之JavaScript设计模式11(外观模式)

    经过一段时间的学习与分享,我们对创建型设计模式已经有了一定的认识,未来的一段时间里我们将展开新的篇章,开始迈入结构性设计模式的学习. 结构性设计模式与创建型设计模式不同,结构性设计模式更偏向于关注如何 ...

  4. 再起航,我的学习笔记之JavaScript设计模式14(桥接模式)

    桥接模式 桥接模式(Bridge): 在系统沿着多个维度变化的同时,又不增加其复杂度并已达到解耦 从定义上看桥接模式的定义十分难以理解,那么我们来通过示例来演示什么是桥接模式. 现在我们需要做一个导航 ...

  5. 再起航,我的学习笔记之JavaScript设计模式17(模板方法模式)

    模板方法模式 由模板方法模式开始我们正式告别结构型设计模式,开始行为型设计模式的学习分享 行为型设计模式用于不同对象之间职责划分或算法抽象,行为型设计模式不仅仅涉及类和对象,还涉及类或对象之间的交流模 ...

  6. 再起航,我的学习笔记之JavaScript设计模式20(策略模式)

    策略模式 策略模式(Strategy):将定义的一组算法封装起来,使其相互之间可以替换.封装的算法具有一定的独立性,不会随客户端变化而变化. 其实策略模式在我们生活中可应用的地方还是比较多的,比如在商 ...

  7. 再起航,我的学习笔记之JavaScript设计模式22(访问者模式)

    访问者模式 概念介绍 访问者模式(Visitor): 针对于对象结构中的元素,定义在不改变该对象的前提下访问结构中元素的新方法 解决低版本IE兼容性 我们来看下面这段代码,这段代码,我们封装了一个绑定 ...

  8. 再起航,我的学习笔记之JavaScript设计模式24(备忘录模式)

    备忘录模式 概念介绍 备忘录模式(Memento): 在不破坏对象的封装性的前提下,在对象之外捕获并保存该对象内部的状态以便日后对象使用或者对象恢复到以前的某个状态. 简易分页 在一般情况下我们需要做 ...

  9. 再起航,我的学习笔记之JavaScript设计模式26(解释器模式)

    解释器模式 概念介绍 解释器模式(Interpreter):给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子. 获取元素在页面中的路径 我们都知道获取一个 ...

随机推荐

  1. WeQuant交易策略—Dual Thrust

    Dual Thrust策略 策略介绍 Dual Thrust是一个趋势跟踪系统,由Michael Chalek在20世纪80年代开发,曾被Future Thruth杂志评为最赚钱的策略之一. Dual ...

  2. BotVS开发基础—2.4 获取订单、取消订单、获取未完成订单

    代码 RetryDelay = 1500; def CancelPendingOrders(exch, orderType): # 取消所有未完成的挂单, 参数1 交易所 参数2 类型 global ...

  3. .net core 2.0 登陆权限验证

    首先在Startup的ConfigureServices方法添加一段权限代码 services.AddAuthentication(x=> { x.DefaultAuthenticateSche ...

  4. c++ new 的相关

    首先是一个链接  这里 说的很详细了 http://www.cnblogs.com/alephsoul-alephsoul/archive/2012/10/17/2728019.html 关于c++ ...

  5. python3 安装及项目管理安装

    python3 一.下载安装 地址:https://www.python.org/downloads/ 安装:傻瓜式安装:我的目录如下 二.环境配置 [右键计算机]-->[属性]-->[高 ...

  6. Python 的经典入门书籍有哪些?

    是不是很多人跟你说,学Python开发就该老老实实地找书来看,再配合死命敲代码?电脑有了,软件也有了,心也收回来了?万事俱备,唯独只欠书籍?没找到到合适的书籍?可以看看这些. 1.Python基础教程 ...

  7. 解决在linux环境下面不显示验证码的问题

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt235 解决在linux环境下面不显示验证码的问题1.tomcat      ...

  8. 求N个元素的子集合个数

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt406 一个集合有n个元素,请问怎么算出来它的子集(包括空集和本身)是 2的n ...

  9. 2017 ACM/ICPC Asia Regional Shenyang Online spfa+最长路

    transaction transaction transaction Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 132768/1 ...

  10. IPsec_VPN实现技术【转载】

    GRE Tunnel GRE Tunnel(General Routing Encapsulation 通用路由封装)是一种非常简单的VPN(Virtual Private Network 虚拟专用网 ...