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 相关文章推荐
编写js扩展方法判断一个数组中是否包含某个元素
Nov 08 Javascript
js获取下拉列表的值和元素个数示例
May 07 Javascript
Javascript原型链和原型的一个误区
Oct 22 Javascript
jQuery仿京东商城楼梯式导航定位菜单
Jul 25 Javascript
weUI应用之JS常用信息提示弹层的封装
Nov 21 Javascript
bootstrap侧边栏圆点导航
Jan 11 Javascript
关于vue.js组件数据流的问题
Jul 26 Javascript
vue 每次渲染完页面后div的滚动条保持在最底部的方法
Mar 17 Javascript
微信小程序仿美团城市选择
Jun 06 Javascript
Vue2.0点击切换类名改变样式的方法
Aug 22 Javascript
JavaScript计算正方形面积
Nov 26 Javascript
JS对象属性的检测与获取操作实例分析
Mar 17 Javascript
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 向访客和爬虫显示不同的内容
2009/11/09 PHP
PHP stream_context_create()作用和用法分析
2011/03/29 PHP
php实现图片以base64显示的方法
2016/10/13 PHP
PHP之认识(二)关于Traits的用法详解
2019/04/11 PHP
javascript Zifa FormValid 0.1表单验证 代码打包下载
2007/06/08 Javascript
javascript开发中因空格引发的错误
2010/11/08 Javascript
jQuery对象初始化的传参方式
2015/02/26 Javascript
JavaScript基本语法学习教程
2016/01/14 Javascript
浅析Javascript中bind()方法的使用与实现
2016/04/29 Javascript
Angularjs的ng-repeat中去除重复数据的方法
2016/08/05 Javascript
AngularJS 模块化详解及实例代码
2016/09/14 Javascript
JS调用某段SQL语句的方法
2016/10/20 Javascript
基于nodejs 的多页面爬虫实例代码
2017/05/31 NodeJs
javascript 缓冲运动框架的实现
2017/09/29 Javascript
详解Vue CLI3配置解析之css.extract
2018/09/14 Javascript
每周一练 之 数据结构与算法(Stack)
2019/04/16 Javascript
javascript+Canvas实现画板功能
2020/06/23 Javascript
[52:20]DOTA2-DPC中国联赛正赛 SAG vs XGBO3 第一场 3月5日
2021/03/11 DOTA
python复制文件到指定目录的实例
2018/04/27 Python
Python lambda表达式用法实例分析
2018/12/25 Python
python 实现12bit灰度图像映射到8bit显示的方法
2019/07/08 Python
python连接PostgreSQL数据库的过程详解
2019/09/18 Python
Django实现从数据库中获取到的数据转换为dict
2020/03/27 Python
CSS3制作圆角图片和椭圆形图片
2016/07/08 HTML / CSS
HTML5自定义data-* data(obj)属性和jquery的data()方法的使用
2012/12/13 HTML / CSS
BRASTY捷克:购买香水、化妆品、手袋和手表
2017/07/12 全球购物
英国独特的时尚和生活方式品牌:JOY
2018/03/17 全球购物
新闻网站实习自我鉴定
2013/09/25 职场文书
自动化系在校本科生求职信
2013/10/23 职场文书
《骑牛比赛》教后反思
2014/04/22 职场文书
校园安全演讲稿
2014/05/09 职场文书
2014年大堂经理工作总结
2014/11/21 职场文书
劳模事迹材料范文
2014/12/24 职场文书
2019最新激励员工口号大全!
2019/06/28 职场文书
《蓝鲸的眼睛》读后感5篇
2020/01/15 职场文书
css3手动实现pc端横向滚动
2022/06/21 HTML / CSS