再起航,我的学习笔记之JavaScript设计模式25(迭代器模式)
迭代器模式
概念介绍
迭代器模式(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(迭代器模式)的更多相关文章
- 再起航,我的学习笔记之JavaScript设计模式08(建造者模式)
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...
- 再起航,我的学习笔记之JavaScript设计模式09(原型模式)
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 我们 ...
- 再起航,我的学习笔记之JavaScript设计模式11(外观模式)
经过一段时间的学习与分享,我们对创建型设计模式已经有了一定的认识,未来的一段时间里我们将展开新的篇章,开始迈入结构性设计模式的学习. 结构性设计模式与创建型设计模式不同,结构性设计模式更偏向于关注如何 ...
- 再起航,我的学习笔记之JavaScript设计模式14(桥接模式)
桥接模式 桥接模式(Bridge): 在系统沿着多个维度变化的同时,又不增加其复杂度并已达到解耦 从定义上看桥接模式的定义十分难以理解,那么我们来通过示例来演示什么是桥接模式. 现在我们需要做一个导航 ...
- 再起航,我的学习笔记之JavaScript设计模式17(模板方法模式)
模板方法模式 由模板方法模式开始我们正式告别结构型设计模式,开始行为型设计模式的学习分享 行为型设计模式用于不同对象之间职责划分或算法抽象,行为型设计模式不仅仅涉及类和对象,还涉及类或对象之间的交流模 ...
- 再起航,我的学习笔记之JavaScript设计模式20(策略模式)
策略模式 策略模式(Strategy):将定义的一组算法封装起来,使其相互之间可以替换.封装的算法具有一定的独立性,不会随客户端变化而变化. 其实策略模式在我们生活中可应用的地方还是比较多的,比如在商 ...
- 再起航,我的学习笔记之JavaScript设计模式22(访问者模式)
访问者模式 概念介绍 访问者模式(Visitor): 针对于对象结构中的元素,定义在不改变该对象的前提下访问结构中元素的新方法 解决低版本IE兼容性 我们来看下面这段代码,这段代码,我们封装了一个绑定 ...
- 再起航,我的学习笔记之JavaScript设计模式24(备忘录模式)
备忘录模式 概念介绍 备忘录模式(Memento): 在不破坏对象的封装性的前提下,在对象之外捕获并保存该对象内部的状态以便日后对象使用或者对象恢复到以前的某个状态. 简易分页 在一般情况下我们需要做 ...
- 再起航,我的学习笔记之JavaScript设计模式26(解释器模式)
解释器模式 概念介绍 解释器模式(Interpreter):给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子. 获取元素在页面中的路径 我们都知道获取一个 ...
随机推荐
- VMware Workstation 12 Player之安装林耐斯-Linux Red Hat 7 -系统
Linux系统之Red Hat 7 安装笔记... Red Hat(红帽)公司(NYSE:RHT)是一家开源解决方案供应商,也是标准普尔500指数成员.总部位于美国北卡罗来纳州的罗利市,截止2015年 ...
- SpringMVC(三)-- 视图和视图解析器、数据格式化标签、数据类型转换、SpringMVC处理JSON数据、文件上传
1.视图和视图解析器 请求处理方法执行完成后,最终返回一个 ModelAndView 对象 对于那些返回 String,View 或 ModeMap 等类型的处理方法,SpringMVC 也会在内部将 ...
- [2014-12-30]如何动态构造Lambda表达式(动态构造Lambda查询条件表达式)
声明 本文对Lambda表达式的扩展,示例代码来源于网络. 场景描述 web开发查询功能的时候,如果查询条件比较多,就会遇到动态组合查询条件的情况.在手写sql的情况下,我们一般会根据传入的参数,针对 ...
- Android binder机制---概述
1.进程间通讯的原因 目前操作系统都使用虚拟存储技术,管理内存. 假设是32位机器,0-3G是用户空间,3-4G是系统使用.虚拟内存和逻辑内存都按4K分页.这样虚拟内存和逻辑内存就存在对应关系. 一个 ...
- java TreeSet 应用
本文主要是介绍一下java集合中的比较重要的Set接口下的可实现类TreeSet TreeSet类,底层用二叉树的数据结构 * 集合中以有序的方式插入和抽取元素. * 添加到TreeSet中的元素必须 ...
- UEditor Flash文件上传-crossdomain.xml文件配置
在使用UEditor富文本时,如果客户端的浏览器是低版本浏览器,如IE7.IE8等,UEditor的文件上传方式将会使用flash方式上传而不是html5,而flash在跨域时唯一的限制策略就是cro ...
- 学习js函数--自执行函数
我在写代码时候经常会在tpl的<script>里写类似的代码: $(function(){ alert("我好饿"); }); 刚开始的时候只知道写了它不需要调用,直接 ...
- jQuery EasyUI弹出确认对话框(确认操作中.....)
因为毕业设计的原因,在初期设计系统的时候没有考虑功能的正确性,所以很多的功能都没有加验证和确认的操作,给人在操作方面上有一些不好的感觉(可能失误点击后,数据就别删除,或者增加了),所以在网上找了一些资 ...
- echarts柱状图修改背景线为网格线、去掉刻度标签、鼠标悬停在柱条上时变色、柱条圆角弧度、
option = { color: ['red'],//修改柱条颜色 tooltip : { triggerOn:'mousemove' }, grid: { left: '3%', right: ' ...
- java-多个数的和
目的:实现多个整数相加. 思路:1.首先要确定用户所需整数的个数n,此部分由用户在键盘上输入. 2.创建一个长度为n的数组. 3.用户从键盘上输入n个整数并判断是否输入正确,正确则存入数组,否则重新输 ...