迭代器模式

概念介绍

迭代器模式(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. 《Java从入门到放弃》JavaSE篇:综合练习——单身狗租赁系统(数组版)

    因为现在只学习了基本语法,所以在综合练习之前,先补充关于方法概念. 方法的作用:把一系列的代码放在一起,然后再取个别名.之后通过这个别名的调用,就相当于执行了这一系列的代码. 方法的语法:([]中的内 ...

  2. C#使用Xamarin开发可移植移动应用进阶篇(9.混淆代码,防止反编译)

    前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github.com/l2999019/DemoApp 可以Star一下,随意 - - 说点什么.. 今天讲讲如 ...

  3. CRF技能词识别过程

    最近在用CRF做未登录技能词识别,虽然艰难,但是感觉很爽,效率非常高. (1)数据准备: 选取30000行精语料作为训练数据.每一个br作为一条数据.使用已有的技能词典对数据进行无标注分词. (2)训 ...

  4. 求最长公共前缀和后缀—基于KMP的next数组

    KMP算法最主要的就是计算next[]算法,但是我们知道next[]求的是当前字符串之前的子字符串的最大前后缀数,但是有的时候我们需要比较字符串中前后缀最大数,比如 LeetCode的shortest ...

  5. cocos quick lua 输入框点击穿透的问题处理方案。

    条件:当前版本quick-3.3 -lua,系统 win7. 问题:在输入框(textField或者editbox,下文"输入框"就代表这两种)打开的情况下弹出其他界面盖住输入框, ...

  6. [置顶] Chat Room:基于JAVA Socket的聊天室设计

    d0304 更新功能实现 d0312 更新部分图片&UI设计部分 d0318 更新功能实现 d1222 实现添加好友功能.实现注册功能.修改大量BUG github:https://githu ...

  7. Java Object中的equals和hashCode

    Java的Object对象中有两个方法比较有意思,一个是equals(),一个是hashCode(),那么这两个的作用有些同学可能还不是很清楚,那么同学们现在就进一步了解一下吧. 下面咱们写一个简单的 ...

  8. 【深入Java虚拟机】之七:Javac编译与JIT编译

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/18009455 编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理 ...

  9. 【集美大学1411_助教博客】团队作业5——测试与发布(Alpha版本)

    同学们好像都进入了状态,任务都完成的不错,测试与发布是一个软件的非常重要的环节,每年双11前夕是阿里巴巴加班最严重的时期,这是因为他们在不断的测试,因为他们不想在双11到来之时有任何差池.所以无论你的 ...

  10. 201521123091 《Java程序设计》第9周学习总结

    Java 第九周总结 第九周的作业. 目录 1.本章学习总结 2.Java Q&A 3.码云上代码提交记录及PTA实验总结 1.本章学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异 ...