zoukankan      html  css  js  c++  java
  • 再起航,我的学习笔记之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设计模式一书,让我们一点点积累一点点成长,希望对大家有所帮助。

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

  • 相关阅读:
    Business English E-mail
    向数据库更新数据
    遍历DataTable
    Convert SqlDataReader to DataTable
    (十七)map、flatMap和reduce方法的补充
    (4)六顶思考帽
    (3)学习方法思维导图
    (十六)call、apply、bind的实现以及区别
    (十五)关于继承
    (十四)防抖和节流
  • 原文地址:https://www.cnblogs.com/chen-jie/p/JavaScript-Iterator.html
Copyright © 2011-2022 走看看