自定義事件,就是自己定義事件類型,自己定義事件處理函數。
我們平時操作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");