Javascript自定义事件详解
javascript自定义事件,其本质就是观察者模式(又称订阅/发布模式),它的好处就是将绑定事件和触发事件相互隔离开,并且可以动态的添加、删除事件。
下面通过实例,一步一步构建一个具体的javascript自定义事件对象。
如:我有一个action1函数,我想每次在执行完action1后,触发另一个函数service1,那么代码我们可以这么写:
//服务service1 function service1(){ } //函数action1 function action1(){ //other things //then 启动service1 service1(); }
good,但是现在想法变了,我想在action1完成后,不仅触发service1,还要触发service2和service3。
按照刚才的思路,在函数action1完成后,顺带加上它们就是了。
如下:
function service1(){} function service2(){} function service3(){} function action1(){ //other things service1(); service2(); service3(); }
但,想法又再次发生波动,在执行完action1函数后,我突然想动态添加一个service4,且,发现service2似乎毫无意义,我不想触发了,怎么办呢?
你可能会说去掉service2,然后在action1后面加入service4不就完了吗?
但是,在真正的项目开发代码日益剧增的情况下,谈何容易,还要去找到相关代码进行操作。
那怎么办呢?
初步想法,定义一个数组嘛(如:servicearray),用来管理所有的service。
当action1执行到末尾后,遍历一遍这个数组函数(servicearray),就欧克了嘛。
且,倘若我们不想运行service2了,便将它从这个数组中删除就好了;倘若想再添加一个新的service,将其追加到servicearray数组中就好了。
如此nice,如下:
var servicearray = []; function service1(){} function service2(){} function service3(){} //将所有service添加到servicearray中 servicearray.push(service1); servicearray.push(service2); servicearray.push(service3); //del:用于删除一个指定的service function del(arr, fn){ for(var i = 0; i < arr.length; i++){ if( arr[i] == fn ){ arr.splice(i,1); break; } } } //action1后,执行所有的service function action1(){ //other things //遍历serviceaary,执行所有service函数。(servicearray在action1内) for(var i =0; i < servicearray.length; i++){ servicearray[i](); } } //添加service4 function service4(){} servicearray.push(service4); //删除service2 del(servicearray, service2);
上面代码挺欧克的,但,复用性一点都不强,且servicearray与action你中有我,我中有你,不好。我们再来优化优化。
代码如下:
var servicearray = []; function service1(){} function service2(){} function service3(){} servicearray.push(service1); servicearray.push(service2); servicearray.push(service3); function del(arr, fn){ for(var i = 0; i < arr.length; i++){ if( arr[i] == fn ){ arr.splice(i,1); break; } } } //添加一个service4 function service4(){} servicearray.push(service4); //删除一个service2 del(servicearray, service2); //添加一个触发函数hanldeaction,分离action与service function hanldeaction(actionname,servicearr){ if(typeof actionname === 'function'){ actionname(); for(var i =0; i < servicearr.length; i++){ servicearr[i](); } } } //执行 handleaction(action1,servicearray);
上面的代码和回调函数有异曲同工之处,因为我们想达到的效果是在 action执行完成之后,运行一系列service嘛。
但,我现在改变想法了,我想在action执行之前执行一系列service呢,或者action中呢。看来得改hanldeaction回调函数啊,这放在项目中反复修改,显然不行。
所以,我们得让其更强大才好。(我想让它在什么地方执行就执行)
如下:
function service1(){} function service2(){} function service3(){} var servicearray = []; servicearray.push(service1); servicearray.push(service2); servicearray.push(service3); function del(arr, fn){ for(var i = 0; i < arr.length; i++){ if( arr[i] == fn ){ arr.splice(i,1); break; } } } //添加一个service4 function service4(){} servicearray.push(service4); //删除一个service2 del(servicearray, service2); /* actionobj用于存储所有action与service关联的对象。 如:{ action1:[service1,service2], action2:[...] } */ var actionobj = {}; /* 修改代码,增加一个actionname与servicearr关联事件。 如,action1关联所有service,这样再结合下方的trigger事件就完美了 params: actionname --> actionobj的属性 servicearr --> 包含所有与actionname相关的service数组 */ function addaction(actionname, servicearr){ if(typeof actionobj[actionname] === 'undefined' ){ actionobj[actionname] = []; } if(typeof servicearr === 'object'){ actionobj[actionname].push(servicearr); } } /* 修改代码,增加一个触发actionname事件 如,当我想触发action1中的所有service时,调用trigger(action1)就ok啦 */ function trigger( actionname ){ var act = actionobj[actionname]; if(act instanceof array){ for(var i = 0, len = act.length; i < len; i++){ for(var j =0, arrlen = act[i].length; j++){ ((act[i])[j])(); } } } }
上述代码是可以,但,有个性能问题,addaction中添加到actionobj[actionname]中的是一个数组,其实完全可以将定义的servicearray数组(为了存储不同的service而声明的数组)移除,转而将每个service直接push进actionobj[actionname]声明的数组中,这样trigger事件效率也得到了提高,从原来的两层for循环降到一层for循环。且,我们再加一个删除service的方法remove。
整理代码如下:
var actionobj = {}; //修改代码,增加一个actionname与service函数直接关联事件 function addaction(actionname, fn){ if(typeof actionobj[actionname] === 'undefined' ){ actionobj[actionname] = []; } if(typeof fn === 'function'){ actionobj[actionname].push(fn); } } //修改代码,增加一个触发actionname事件 function trigger( actionname ){ var actionarray = actionobj[actionname]; if(actionarray instanceof array){ for(var i = 0, len = actionarray.length; i < len; i++){ if(typeof actionarray[i] === 'function'){ actionarray[i](); } } } } //修改代码,增加一个删除actionname中的service事件 function remove(actionname, fn){ var actionarray = actionobj[actionname]; if(typeof actionname === 'string' && actionarray instanceof array){ if(typeof fn === 'function'){ //清除actionname中对应的fn方法 for(var i=0, len = actionarray.length; i < len; i++){ if(actionarray[i] === fn){ actionobj[actionname].splice(i,1); } } } } }
上面的代码好是好,action与service也互不影响,也完成了它的使命。
使命?
这就是我们一起编写的自定义事件嘛。是不是很简单。
哈哈哈,我尼玛也在代码中用到设计模式了(观察者模式)。
一鼓作气,我们再来优化下上面的代码。有没有注意,我们是使用的全局变量,在模块化开发的大环境下,我们居然在用全局变量,岂不是污染命名空间嘛。再改改。
修改代码如下:
var eventtarget = function(){ this.listener = {}; } eventtarget.prototype = { constructor:eventtarget, addaction: function(actionname, fn){ if(typeof actionname === 'string' && typeof fn === 'function'){ //如果不存在actionname,就新建一个 if(typeof this.listener[actionname] === 'undefined'){ this.listener[actionname] = [fn]; } //否则,直接往相应actinoname里面塞 else{ this.listener[actionname].push(fn); } } }, trigger: function(actionname){ var actionarray = this.listener[actionname]; //触发一系列actionname里的函数 if(actionarray instanceof array){ for(var i = 0, len = actionarray.length; i < len; i++){ if(typeof actionarray[i] === 'function'){ actionarray[i](); } } } actionarray = null; }, remove: function(actionname, fn){ var actionarray = this.listener[actionname]; if(typeof actionname === 'string' && actionarray instanceof array){ if(typeof fn === 'function'){ //清除actionname中对应的fn方法 for(var i=0, len = actionarray.length; i < len; i++){ if(actionarray[i] === fn){ this.listener[actionname].splice(i,1); } } } } actionarray = null; } };
一个javascript自定义事件新鲜出炉。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: Linux 检测服务器是否连接着网络
下一篇: 简单的Python2.7编程初学经验总结