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 相关文章推荐
为数据添加append,remove功能
Oct 03 Javascript
Prototype Date对象 学习
Jul 12 Javascript
jQuery 下拉列表 二级联动插件分享
Mar 29 Javascript
单击浏览器右上角的X关闭窗口弹出提示的小例子
Jun 12 Javascript
动态加载jquery库的方法
Feb 12 Javascript
js函数内变量的作用域分析
Jan 12 Javascript
Jquery数字上下滚动动态切换插件
Aug 08 Javascript
JS根据浏览器窗口大小实时动态改变网页文字大小的方法
Feb 25 Javascript
jQuery实现的省市联动菜单功能示例【测试可用】
Jan 13 Javascript
ES6学习笔记之Set和Map数据结构详解
Apr 07 Javascript
阿望教你用vue写扫雷小游戏
Jan 20 Javascript
详解vue高级特性
Jun 09 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
ftp类(example.php)
2006/10/09 PHP
基于mysql的论坛(3)
2006/10/09 PHP
php实现utf-8和GB2312编码相互转换函数代码
2013/02/07 PHP
PHP浮点数精度问题汇总
2015/05/13 PHP
jQuery 获取URL的GET参数值的小例子
2013/04/18 Javascript
JQuery的ready函数与JS的onload的区别详解
2013/11/21 Javascript
jQuery滚动条插件nanoscroller使用指南
2015/04/21 Javascript
JavaScript中数据结构与算法(二):队列
2015/06/19 Javascript
详解js运算符单竖杠“|”与“||”的用法和作用介绍
2016/11/04 Javascript
jQuery移除或禁用html元素点击事件常用方法小结
2017/02/10 Javascript
angularjs的select使用及默认选中设置
2017/04/08 Javascript
Angualrjs 表单验证的两种方式(失去焦点验证和点击提交验证)
2017/05/09 Javascript
Angular.js前台传list数组由后台spring MVC接收数组示例代码
2017/07/31 Javascript
一步步教你利用webpack如何搭一个vue脚手架(超详细讲解和注释)
2018/01/08 Javascript
详解vue中router-link标签所必备了解的属性
2019/04/15 Javascript
koa-router路由参数和前端路由的结合详解
2019/05/19 Javascript
Vue实现商品飞入购物车效果(电商项目)
2019/11/26 Javascript
three.js 制作动态二维码的示例代码
2020/07/31 Javascript
js实现简单的无缝轮播效果
2020/09/05 Javascript
pandas的object对象转时间对象的方法
2018/04/11 Python
Python使用matplotlib绘制三维图形示例
2018/08/25 Python
对python中数组的del,remove,pop区别详解
2018/11/07 Python
Python 实现自动导入缺失的库
2019/10/29 Python
什么是Python变量作用域
2020/06/03 Python
keras导入weights方式
2020/06/12 Python
浅谈Selenium 控制浏览器的常用方法
2020/12/04 Python
历史系自荐信范文
2013/12/24 职场文书
大班亲子运动会方案
2014/06/10 职场文书
安全例会汇报材料
2014/08/23 职场文书
公务员爱岗敬业演讲稿
2014/08/26 职场文书
我们的节日端午节活动总结
2015/02/11 职场文书
2015暑假社会调查报告
2015/07/13 职场文书
委托开发合同书(标准版)
2019/08/07 职场文书
vue中data改变后让视图同步更新的方法
2021/03/29 Vue.js
详解Vue的options
2021/05/15 Vue.js
Python数据可视化之Seaborn的安装及使用
2022/04/19 Python