JS自定义事件
自定义事件,就是自己定义事件类型,自己定义事件处理函数。
我们平时操作dom时经常会用到onclick、onmousemove等浏览器特定行为的事件类型。
封装is自定义事件基本的构思:
var eventTarget = { addEvent: function(){ //添加事件 }, fireEvent: function(){ //触发事件 }, removeEvent: function(){ //移除事件 } };
在js默认事件中事件类型以及对应的执行函数是一一对应的,但是自定义事件,需要一个映射表来建立两者之间的联系。
如: 这样每个类型可以处理多个事件函数
handlers = { "type1":[ "fun1", "fun2", // "..." ], "type2":[ "fun1", "fun2" // "..." ] //"..." }
代码实现:
function EventTarget(){ //事件处理程序数组集合 this.handlers={}; } //自定义事件的原型对象 EventTarget.prototype={ //设置原型构造函数链 constructor:EventTarget, //注册给定类型的事件处理程序 //type->自定义事件类型,如click,handler->自定义事件回调函数 addEvent:function(type,handler){ //判断事件处理函数中是否有该类型事件 if(this.handlers[type]==undefined){ this.handlers[type]=[]; } this.handlers[type].push(handler); }, //触发事件 //event为一个js对象,属性中至少包含type属性。 fireEvent:function(event){ //模拟真实事件的event if(!event.target){ event.target=this; } //判断是否存在该事件类型 if(this.handlers[event.type] instanceof Array){ var items=this.handlers[event.type]; //在同一事件类型下可能存在多个事件处理函数,依次触发 //执行触发 items.forEach(function(item){ item(event); }) } }, //删除事件 removeEvent:function(type,handler){ //判断是否存在该事件类型 if(this.handlers[type] instanceof Array){ var items=this.handlers[type]; //在同一事件类型下可能存在多个处理事件 for(var i=0;i<items.length;i++){ if(items[i]==handler){ //从该类型的事件数组中删除该事件 items.splice(i,1); break; } } } } } //调用方法 function fun(){ console.log('执行该方法'); } function fun1(obj){ console.log('run '+obj.min+'s'); } var target=new EventTarget(); target.addEvent("run",fun);//添加事件 target.addEvent("run",fun1);//添加事件 target.fireEvent({type:"run",min:"30"});//执行该方法 123 target.removeEvent("run",fun);//移除事件 target.fireEvent({type:"run",min:"20"});//123
为什么要把方法添加到对象原型上?
在构造函数中加属性,在原型中加方法。
将属性和方法都写在构造函数里是没有问题的,但是每次进行实例化的过程中,要重复创建功能不变的方法。
由于方法本质上是函数,其实也就是在堆内存中又新建了一个对象空间存放存储函数,造成了不必要的资源浪费。
在本身添加会导致每次对象实例化时代码被复制,都需要申请一块内存存放该方法。
写一个EventEmitter类,包括on()、off()、once()、emit()方法
once():为指定事件注册一个单次监听器,单次监听器最多只触发一次,触发后立即解除监听器。
class EventEmitter{ constructor(){ this.handlers={}; } on(type,fn){ if(!this.handlers[type]){ this.handlers[type]=[]; } this.handlers[type].push(fn); return this; } off(type,fn){ let fns=this.handlers[type]; for(let i=0;i<fns.length;i++){ if(fns[i]==fn){ fns.splice(i,1); break; } } return this; } emit(...args){ let type=args[0]; let params=[].slice.call(args,1); let fn=this.handlers[type]; fn.forEach((item)=>{ item.apply(this,params);//执行函数 }) return this; } once(type,fn){ let wrap=(...args)=>{ fn.apply(this,args);//执行事件后删除 this.off(type,wrap); } this.on(type,wrap);//再添加上去 return this; } } let emitter=new EventEmitter(); function fun1(){ console.log('fun1'); } function fun2(){ console.log('fun2'); } function fun3(){ console.log('fun3'); } emitter.on('TEST1',fun1).on('TEST2',fun2).emit('TEST1').once('TEST2',fun3); emitter.emit("TEST2");