迭代器模式

概念介绍

迭代器模式(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,第二天,算术运算

    算术运算 先回顾上次我们提到的编程特性 变量和数据类型,赋值和输出 算术运算 选择结构 循环结构 函数定义,函数调用 变量作用域 栈,程序运行的基石 面向对象 异常处理 语言提供的公用包 第一天我们讲 ...

  2. 使用adb shell启动特定activity

    使用adb shell启动特定activity Android笔记 使用adb shell可以直接运行某个activity,避免调试过程中修改Manifest文件. 1.在AndroidManifes ...

  3. 关于transient和static的序列化和反序列化

    做java开发有段时间了,最近没那么忙了,抽了点时间看了下java的源码 . 在读源码的时候看到了一个 transient 修饰的变量 ,字面意思是瞬变的.在以前的开发过程中也没用到过这个修饰语,查了 ...

  4. TensorFlow框架(5)之机器学习实践

    1. Iris data set Iris数据集是常用的分类实验数据集,由Fisher, 1936收集整理.Iris也称鸢尾花卉数据集,是一类多重变量分析的数据集.数据集包含150个数据集,分为3类, ...

  5. ubuntu下安装rubymine

    1.安装jdk 先查看系统有没有安装jdk,打开终端,输入以下命令: java -version 如果没有安装,在联网的环境下执行: $ -jdk 2.安装rubymine 从官网(http://ww ...

  6. [Spring面试] 问题整理

    1.谈谈你对spring IOC和DI的理解,它们有什么区别? IoC:Inverse of Control 反转控制的概念,就是将原本在程序中手动创建UserService对象的控制权,交由Spri ...

  7. PKIX path building failed

    百度这个问题,很多人都说是证书问题.至于是什么证书呢?搞了老半天原来是 /Java/jre/lib/security/cacerts不正确.换了个jdk版本就解决这个问题了. 可靠的jdk1.8下载链 ...

  8. hashMap遍历方式

    package Ch17; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java ...

  9. android monkey测试学习

    前提是:有安卓环境,能用adb命令 一.Monkey 测试的目的? 该工具可用于测试稳定性. 开发人员结合monkey 打印的日志 和系统打印的日志,解决测试中出现的问题 二.Monkey 测试的特点 ...

  10. Java Integer封装类的IntegerCache内部类

    个人理解,不喜勿喷,欢迎指正. 首先看下面这段代码,猜一下输出结果是什么 Integer a = 10; Integer b = 10; System.out.println(a == b); a = ...