Javascript自定义事件详解


Posted in Javascript onJanuary 13, 2017

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自定义事件新鲜出炉。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript游戏开发之《三国志曹操传》零部件开发(二)人物行走的实现
Jan 23 Javascript
JavaScript利用正则表达式去除日期中的“-”
Jul 01 Javascript
js 判断图片是否加载完以及实现图片的预下载
Aug 14 Javascript
jQuery带箭头提示框tooltips插件集锦
Nov 17 Javascript
延时加载JavaScript代码提高速度
Dec 27 Javascript
JavaScript事件 &quot;事件对象&quot;的注意要点
Jan 14 Javascript
VUEJS实战之利用laypage插件实现分页(3)
Jun 13 Javascript
jQuery复合事件结合toggle()方法的用法示例
Jun 10 jQuery
当vue路由变化时,改变导航栏的样式方法
Aug 22 Javascript
js+css3实现炫酷时钟
Aug 18 Javascript
JavaScript实现显示和隐藏图片
Apr 29 Javascript
分享一个vue实现的记事本功能案例
Apr 11 Vue.js
JavaScript简单生成 N~M 之间随机数的方法
Jan 13 #Javascript
微信小程序 增、删、改、查操作实例详解
Jan 13 #Javascript
jQuery动态生成表格及右键菜单功能示例
Jan 13 #Javascript
Vue数据驱动模拟实现5
Jan 13 #Javascript
利用HTML5+Socket.io实现摇一摇控制PC端歌曲切换
Jan 13 #Javascript
使用jQuery的ajax方法向服务器发出get和post请求的方法
Jan 13 #Javascript
jquery,js简单实现类似Angular.js双向绑定
Jan 13 #Javascript
You might like
PHP中用header图片地址 简单隐藏图片源地址
2008/04/09 PHP
php保存信息到当前Session的方法
2015/03/16 PHP
php基于curl主动推送最新内容给百度收录的方法
2016/10/14 PHP
PHP未登录自动跳转到登录页面
2016/12/21 PHP
php制作圆形用户头像的实例_自定义封装类源代码
2017/09/18 PHP
[原创]站长必须要知道的javascript广告代码
2007/05/30 Javascript
js 为label标签和div标签赋值的方法
2013/08/08 Javascript
node.js中的console.timeEnd方法使用说明
2014/12/09 Javascript
jQuery插件kinMaxShow扩展效果用法实例
2015/05/04 Javascript
简介JavaScript中valueOf()方法的使用
2015/06/05 Javascript
JS实现具备延时功能的滑动门菜单效果
2015/09/17 Javascript
为何JS操作的href都是javascript:void(0);呢
2015/11/12 Javascript
Bootstrap每天必学之栅格系统(布局)
2015/11/25 Javascript
JS弹出层遮罩,隐藏背景页面滚动条细节优化分析
2016/04/29 Javascript
用原生JS对AJAX做简单封装的实例代码
2016/07/13 Javascript
关于JavaScript和jQuery的类型判断详解
2016/10/08 Javascript
jQuery Easy UI中根据第一个下拉框选中的值设置第二个下拉框是否可以编辑
2016/11/29 Javascript
利用node.js如何创建子进程详解
2017/12/09 Javascript
Vue仿今日头条实例详解
2018/02/06 Javascript
vue router 用户登陆功能的实例代码
2019/04/24 Javascript
javascript数组元素删除方法delete和splice解析
2019/12/09 Javascript
[01:34]2014DOTA2展望TI 剑指西雅图VG战队专访
2014/06/30 DOTA
Python获取单个程序CPU使用情况趋势图
2015/03/10 Python
Python3实现并发检验代理池地址的方法
2016/09/18 Python
Django Admin 实现外键过滤的方法
2017/09/29 Python
Python多线程原理与用法实例剖析
2019/01/22 Python
python连接PostgreSQL数据库的过程详解
2019/09/18 Python
CSS实现雨滴动画效果的实例代码
2019/10/08 HTML / CSS
无谷物狗粮:Pooch & Mutt
2018/05/23 全球购物
幼儿园教师师德师风演讲稿:爱我所爱 无悔青春
2014/09/10 职场文书
个人委托书范本汇总
2014/10/01 职场文书
党性分析自查总结
2014/10/14 职场文书
环保建议书作文400字
2015/09/14 职场文书
pytorch显存一直变大的解决方案
2021/04/08 Python
Go语言设计模式之结构型模式
2021/06/22 Golang
一篇文章带你了解Python和Java的正则表达式对比
2021/09/15 Python