基于JavaScript實現一個簡單的事件觸發(fā)器
說在前面
簡單實現一個事件觸發(fā)器
題目描述
設計一個 EventEmitter
類。這個接口與 Node.js 或 DOM 的 Event Target 接口相似,但有一些差異。EventEmitter
應該允許訂閱事件和觸發(fā)事件。
你的 EventEmitter
類應該有以下兩個方法:
subscribe - 這個方法接收兩個參數:一個作為字符串的事件名和一個回調函數。當事件被觸發(fā)時,這個回調函數將被調用。 一個事件應該能夠有多個監(jiān)聽器。當觸發(fā)帶有多個回調函數的事件時,應按照訂閱的順序依次調用每個回調函數。應返回一個結果數組。你可以假設傳遞給 subscribe
的回調函數都不是引用相同的。 subscribe
方法還應返回一個對象,其中包含一個 unsubscribe
方法,使用戶可以取消訂閱。當調用 unsubscribe
方法時,回調函數應該從訂閱列表中刪除,并返回 undefined。
emit - 這個方法接收兩個參數:一個作為字符串的事件名和一個可選的參數數組,這些參數將傳遞給回調函數。如果沒有訂閱給定事件的回調函數,則返回一個空數組。否則,按照它們被訂閱的順序返回所有回調函數調用的結果數組。
示例 1:
//輸入: actions = ["EventEmitter", "emit", "subscribe", "subscribe", "emit"], values = [[], ["firstEvent", "function cb1() { return 5; }"], ["firstEvent", "function cb1() { return 5; }"], ["firstEvent"]] //輸出: [[],["emitted",[]],["subscribed"],["subscribed"],["emitted",[5,6]]] //解釋: const emitter = new EventEmitter(); emitter.emit("firstEvent"); // [], 還沒有訂閱任何回調函數 emitter.subscribe("firstEvent", function cb1() { return 5; }); emitter.subscribe("firstEvent", function cb2() { return 6; }); emitter.emit("firstEvent"); // [5, 6], 返回 cb1 和 cb2 的輸出
示例 2:
//輸入: actions = ["EventEmitter", "subscribe", "emit", "emit"], values = [[], ["firstEvent", "function cb1(...args) { return args.join(','); }"], ["firstEvent", [1,2,3]], ["firstEvent", [3,4,6]]] //輸出: [[],["subscribed"],["emitted",["1,2,3"]],["emitted",["3,4,6"]]] //解釋: 注意 emit 方法應該能夠接受一個可選的參數數組。 const emitter = new EventEmitter(); emitter.subscribe("firstEvent, function cb1(...args) { return args.join(','); }); emitter.emit("firstEvent", [1, 2, 3]); // ["1,2,3"] emitter.emit("firstEvent", [3, 4, 6]); // ["3,4,6"]
示例 3:
//輸入: actions = ["EventEmitter", "subscribe", "emit", "unsubscribe", "emit"], values = [[], ["firstEvent", "(...args) => args.join(',')"], ["firstEvent", [1,2,3]], [0], ["firstEvent", [4,5,6]]] //輸出: [[],["subscribed"],["emitted",["1,2,3"]],["unsubscribed",0],["emitted",[]]] //解釋: const emitter = new EventEmitter(); const sub = emitter.subscribe("firstEvent", (...args) => args.join(',')); emitter.emit("firstEvent", [1, 2, 3]); // ["1,2,3"] sub.unsubscribe(); // undefined emitter.emit("firstEvent", [4, 5, 6]); // [], 沒有訂閱者
示例 4:
//輸入: actions = ["EventEmitter", "subscribe", "subscribe", "unsubscribe", "emit"], values = [[], ["firstEvent", "x => x + 1"], ["firstEvent", "x => x + 2"], [0], ["firstEvent", [5]]] //輸出: [[],["subscribed"],["emitted",["1,2,3"]],["unsubscribed",0],["emitted",[7]]] //解釋: const emitter = new EventEmitter(); const sub1 = emitter.subscribe("firstEvent", x => x + 1); const sub2 = emitter.subscribe("firstEvent", x => x + 2); sub1.unsubscribe(); // undefined emitter.emit("firstEvent", [5]); // [7]
提示:
1 <= actions.length <= 10
values.length === actions.length
- 所有測試用例都是有效的。例如,你不需要處理取消一個不存在的訂閱的情況。
- 只有 4 種不同的操作:
EventEmitter
、emit
、subscribe
和unsubscribe
。EventEmitter
操作沒有參數。 emit
操作接收 1 或 2 個參數。第一個參數是要觸發(fā)的事件名,第二個參數傳遞給回調函數。subscribe
操作接收 2 個參數,第一個是事件名,第二個是回調函數。unsubscribe
操作接收一個參數,即之前進行訂閱的順序(從 0 開始)。
解題思路
這是一個簡單的事件觸發(fā)器實現,通過subscribe
方法訂閱指定事件并注冊回調函數,通過emit
方法觸發(fā)指定事件,并執(zhí)行所有相關回調函數。此外,subscribe
方法返回一個包含取消訂閱函數的對象,可以在需要取消訂閱時調用。
具體來說,該事件觸發(fā)器實現主要有以下幾個方法:
- constructor:構造函數,創(chuàng)建一個Map實例來存儲事件及其對應的回調函數列表。
- subscribe:訂閱指定事件并注冊回調函數,返回一個包含取消訂閱函數的對象。
- unsubscribe:取消訂閱指定事件的回調函數。
- emit:觸發(fā)指定事件,并執(zhí)行所有相關回調函數。下面是這些方法的詳細說明:
constructor
constructor(){ this.eventMap = new Map(); }
構造函數,創(chuàng)建一個Map實例來存儲事件及其對應的回調函數列表。
subscribe
subscribe(eventName, callback) { const eventList = this.eventMap.get(eventName) || []; const key = Math.ceil(Math.random() * 10000); eventList.push({ key, callback }); this.eventMap.set(eventName,eventList); return { unsubscribe: () => { const eventList = this.eventMap.get(eventName) || []; const index = eventList.findIndex(item=>item.key === key); if(index === -1) return; eventList.splice(index,1); if(eventList.length === 0) this.eventMap.delete(eventName); this.eventMap.set(eventName,eventList); } }; }
訂閱指定事件并注冊回調函數,返回一個包含取消訂閱函數的對象。
該方法首先獲取指定事件對應的回調函數列表,如果不存在則新建一個空列表。然后,生成一個隨機數作為回調函數的唯一鍵,并將其與回調函數一起存入回調函數列表中。最后,更新事件對應的回調函數列表,并返回一個包含unsubscribe方法的對象。
unsubscribe
unsubscribe: () => { const eventList = this.eventMap.get(eventName) || []; const index = eventList.findIndex(item=>item.key === key); if(index === -1) return; eventList.splice(index,1); if(eventList.length === 0) this.eventMap.delete(eventName); this.eventMap.set(eventName,eventList); }
取消訂閱指定事件的回調函數。
該方法首先獲取指定事件對應的回調函數列表,然后在列表中查找指定鍵的回調函數,并將其從列表中刪除。如果刪除后回調函數列表為空,則刪除該事件對應的鍵值對。
emit
emit(eventName, args = []) { const eventList = this.eventMap.get(eventName); if(!eventList) return []; const res = []; eventList.forEach(fn=>{ res.push(fn.callback(...args)); }) return res; }
觸發(fā)指定事件,并執(zhí)行所有相關回調函數。
該方法首先獲取指定事件對應的回調函數列表,如果不存在則返回空數組。然后,對于列表中的每個回調函數,按照順序執(zhí)行其對應的函數,并將返回值存入一個數組中。最后,返回保存了所有返回值的數組。
完整代碼
class EventEmitter { constructor(){ this.eventMap = new Map(); } /** * @param {string} eventName * @param {Function} callback * @return {Object} */ subscribe(eventName, callback) { const eventList = this.eventMap.get(eventName) || []; const key = Math.ceil(Math.random() * 10000); eventList.push({ key, callback }); this.eventMap.set(eventName,eventList); return { unsubscribe: () => { const eventList = this.eventMap.get(eventName) || []; const index = eventList.findIndex(item=>item.key === key); if(index === -1) return; eventList.splice(index,1); if(eventList.length === 0) this.eventMap.delete(eventName); this.eventMap.set(eventName,eventList); } }; } /** * @param {string} eventName * @param {Array} args * @return {Array} */ emit(eventName, args = []) { const eventList = this.eventMap.get(eventName); if(!eventList) return []; const res = []; eventList.forEach(fn=>{ res.push(fn.callback(...args)); }) return res; } } // const emitter = new EventEmitter(); // function onClickCallback() { return 99 } // const sub = emitter.subscribe('onClick', onClickCallback); // let res = emitter.emit('onClick'); // [99] // sub.unsubscribe(); // undefined // res = emitter.emit('onClick'); // []
到此這篇關于基于JavaScript實現一個簡單的事件觸發(fā)器的文章就介紹到這了,更多相關JavaScript事件觸發(fā)器內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
layui 數據表格 點擊分頁按鈕 監(jiān)聽事件的實例
今天小編就為大家分享一篇layui 數據表格 點擊分頁按鈕 監(jiān)聽事件的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09JavaScript使ifram跨域相互訪問及與PHP通信的實例
這篇文章主要介紹了JavaScript使ifram跨域相互訪問及與PHP通信的實例,同時對同域間的訪問也作了詳細的演示,需要的朋友可以參考下2016-03-03