自己實現一個EventEmitter
function MyEvents() { this.events = {};//存放事件監聽函數 this._maxListeners = 10;//一個事件類型增加的函數數量(默認最多10個)}MyEvents.prototype.on = MyEvents.prototype.addListener = function (type,listener) { if(this.events[type]){ //如果有此事件,則將監聽函數推入事件數組 this.events[type].push(listener); if(this._maxListeners!=0 && this.events[type].length >= this._maxListeners){ console.error(超出限制); } }else{ this.events[type] = [listener]; }};MyEvents.prototype.once = function (type,listener) { //只處理一次事件 //wrapper用完一次就銷毀 function wrapper(...rest) { listener.apply(this,rest); this.removeListener(type,wrapper); } this.on(type,wrapper);};MyEvents.prototype.prependListener = function (type,listener) { //添加 listener 函數到名為type的事件的監聽器數組的開頭。不會檢查 listener 是否已被添加。 // 多次調用並傳入相同的 type 和 listener 會導致 listener 被添加與調用多次。 // 返回一個 EventEmitter 引用,可以鏈式調用 if(this.events[type]){ this.events[type].unshift(listener); if(this._maxListeners!=0 && this.events[type].length >= this._maxListeners){ console.error(超出限制); }; }else{ this.events[type] = [listener]; }};MyEvents.prototype.prependOnceListener = function (type,listener) { //添加一個單次 listener 函數到名為 type 的事件的監聽器數組的開頭。 下次觸發 type 事件時,監聽器會被移除,然後調用。返回一個 EventEmitter 引用,可以鏈式調用 function prependWrapper(...rest) { listener.apply(rest); this.removeListener(type,prependWrapper); }; this.prependListener(type,prependWrapper);};MyEvents.prototype.emit = function (type,...rest) { //按監聽器的註冊順序,同步地調用名為type的事件監聽器 if(this.events[type]){ this.events[type].forEach(listener => listener.apply(this,rest)); };};MyEvents.prototype.eventNames = function () { //返回一個列出觸發器已註冊監聽器的事件的數組 //Reflect.ownKeys(obj)返回一個數組,包含對象自身的所有屬性,不管屬性名是Symbol或字元串,也不管是否可枚舉. return Reflect.ownKeys(this.events);};MyEvents.prototype.setMaxListeners = function (num) { //設置當前的最大監聽器限制值 this._maxListeners = num;};MyEvents.prototype.getMaxListeners = function () { //返回 EventEmitter 當前的最大監聽器限制值 return this._maxListeners;};MyEvents.prototype.listenerCount = function (type) { //返回正在監聽名為 type的事件的監聽器的數量。 if(type){ if(this.events[type]){ return this.events[type].length; }else { return new Error(無此類型事件); } }else{ return new Error(無事件名稱); };};MyEvents.prototype.listeners = function (type) { //返回名為 type 的事件的監聽器數組的副本。 return this.events[type];};MyEvents.prototype.removeListener = function (type,listener) { if(this.events[type]){ //如果當前迭代的項目如果不等於參數的話就保留,否則過濾掉 //數組的filter用法 this.events[type] = this.events[type].filter(l=>l!=listener); }};MyEvents.prototype.removeAllListeners = function (type) { //移除某個事件的所有監聽函數 delete this.events[type];};module.exports = MyEvents;
以下為測試代碼:
//此JS用來測試自己實現的EventEmittervar EventEmitter = require(./myEvents);let util = require(util);function Bell() { EventEmitter.call(this);//繼承EventEmitter私有屬性/方法}//util.inherits原型繼承公有方法var sym = Symbol(symbol);util.inherits(Bell,EventEmitter);var bell = new Bell();bell.on(events,first);//這是第1次觸發事件,裡面有value1bell.on(events,two);//這是第1次觸發事件,裡面有value1和value2bell.on(events,three);//這是第1次觸發事件,裡面有value1和value2還有value3bell.on(sym,three);// console.log(bell.listenerCount(events));//3// console.log(bell.eventNames());//[events,Symbol(symbol)]// console.log(bell.listeners(events));//[ [Function: first], [Function: two], [Function: three] ]// bell.emit(events,1,value1,value2,value3);// bell.removeListener(events,three);// console.log(bell.listeners(events));//[ [Function: first], [Function: two] ]// bell.removeAllListeners(events);// console.log(bell.listenerCount(events));//Error:無此類型事件(因為已經移除所有events事件)// 測試prependListener方法// bell.on(foo,a);// bell.on(foo,b);// bell.prependListener(foo,b);// bell.prependListener(foo,c);// bell.emit(foo);//c,b,a,b//事件function first(num,data) { console.log(`這是第${num}次觸發事件,裡面有${data}`)};function two(num,data,dataTwo) { console.log(`這是第${num}次觸發事件,裡面有${data}和${dataTwo}`);};function three(num,data,dataTwo,dataThree) { console.log(`這是第${num}次觸發事件,裡面有${data}和${dataTwo}還有${dataThree}`);};function a() { console.log(a);}function b() { console.log(b);}function c() { console.log(c);}
推薦閱讀:
※誰能詳細解釋下什麼是單頁面?
※AE 動畫直接變原生代碼:Airbnb 發布開源動畫庫 Lottie
※雙向綁定的簡單實現——基於「臟檢測」
※前端,準備年後跳槽,從現在開始準備,該制定怎樣的計劃?