基于JavaScript實(shí)現(xiàn)一個(gè)簡(jiǎn)單的事件觸發(fā)器
說(shuō)在前面
簡(jiǎn)單實(shí)現(xiàn)一個(gè)事件觸發(fā)器
題目描述
設(shè)計(jì)一個(gè) EventEmitter 類(lèi)。這個(gè)接口與 Node.js 或 DOM 的 Event Target 接口相似,但有一些差異。EventEmitter 應(yīng)該允許訂閱事件和觸發(fā)事件。
你的 EventEmitter 類(lèi)應(yīng)該有以下兩個(gè)方法:
subscribe - 這個(gè)方法接收兩個(gè)參數(shù):一個(gè)作為字符串的事件名和一個(gè)回調(diào)函數(shù)。當(dāng)事件被觸發(fā)時(shí),這個(gè)回調(diào)函數(shù)將被調(diào)用。 一個(gè)事件應(yīng)該能夠有多個(gè)監(jiān)聽(tīng)器。當(dāng)觸發(fā)帶有多個(gè)回調(diào)函數(shù)的事件時(shí),應(yīng)按照訂閱的順序依次調(diào)用每個(gè)回調(diào)函數(shù)。應(yīng)返回一個(gè)結(jié)果數(shù)組。你可以假設(shè)傳遞給 subscribe 的回調(diào)函數(shù)都不是引用相同的。 subscribe 方法還應(yīng)返回一個(gè)對(duì)象,其中包含一個(gè) unsubscribe 方法,使用戶可以取消訂閱。當(dāng)調(diào)用 unsubscribe 方法時(shí),回調(diào)函數(shù)應(yīng)該從訂閱列表中刪除,并返回 undefined。
emit - 這個(gè)方法接收兩個(gè)參數(shù):一個(gè)作為字符串的事件名和一個(gè)可選的參數(shù)數(shù)組,這些參數(shù)將傳遞給回調(diào)函數(shù)。如果沒(méi)有訂閱給定事件的回調(diào)函數(shù),則返回一個(gè)空數(shù)組。否則,按照它們被訂閱的順序返回所有回調(diào)函數(shù)調(diào)用的結(jié)果數(shù)組。
示例 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]]]
//解釋?zhuān)?
const emitter = new EventEmitter();
emitter.emit("firstEvent"); // [], 還沒(méi)有訂閱任何回調(diào)函數(shù)
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"]]]
//解釋?zhuān)?注意 emit 方法應(yīng)該能夠接受一個(gè)可選的參數(shù)數(shù)組。
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",[]]]
//解釋?zhuān)?
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]); // [], 沒(méi)有訂閱者
示例 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]]]
//解釋?zhuān)?
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 <= 10values.length === actions.length- 所有測(cè)試用例都是有效的。例如,你不需要處理取消一個(gè)不存在的訂閱的情況。
- 只有 4 種不同的操作:
EventEmitter、emit、subscribe和unsubscribe。EventEmitter操作沒(méi)有參數(shù)。 emit操作接收 1 或 2 個(gè)參數(shù)。第一個(gè)參數(shù)是要觸發(fā)的事件名,第二個(gè)參數(shù)傳遞給回調(diào)函數(shù)。subscribe操作接收 2 個(gè)參數(shù),第一個(gè)是事件名,第二個(gè)是回調(diào)函數(shù)。unsubscribe操作接收一個(gè)參數(shù),即之前進(jìn)行訂閱的順序(從 0 開(kāi)始)。
解題思路
這是一個(gè)簡(jiǎn)單的事件觸發(fā)器實(shí)現(xiàn),通過(guò)subscribe方法訂閱指定事件并注冊(cè)回調(diào)函數(shù),通過(guò)emit方法觸發(fā)指定事件,并執(zhí)行所有相關(guān)回調(diào)函數(shù)。此外,subscribe方法返回一個(gè)包含取消訂閱函數(shù)的對(duì)象,可以在需要取消訂閱時(shí)調(diào)用。
具體來(lái)說(shuō),該事件觸發(fā)器實(shí)現(xiàn)主要有以下幾個(gè)方法:
- constructor:構(gòu)造函數(shù),創(chuàng)建一個(gè)Map實(shí)例來(lái)存儲(chǔ)事件及其對(duì)應(yīng)的回調(diào)函數(shù)列表。
- subscribe:訂閱指定事件并注冊(cè)回調(diào)函數(shù),返回一個(gè)包含取消訂閱函數(shù)的對(duì)象。
- unsubscribe:取消訂閱指定事件的回調(diào)函數(shù)。
- emit:觸發(fā)指定事件,并執(zhí)行所有相關(guān)回調(diào)函數(shù)。下面是這些方法的詳細(xì)說(shuō)明:
constructor
constructor(){
this.eventMap = new Map();
}
構(gòu)造函數(shù),創(chuàng)建一個(gè)Map實(shí)例來(lái)存儲(chǔ)事件及其對(duì)應(yīng)的回調(diào)函數(shù)列表。
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);
}
};
}
訂閱指定事件并注冊(cè)回調(diào)函數(shù),返回一個(gè)包含取消訂閱函數(shù)的對(duì)象。
該方法首先獲取指定事件對(duì)應(yīng)的回調(diào)函數(shù)列表,如果不存在則新建一個(gè)空列表。然后,生成一個(gè)隨機(jī)數(shù)作為回調(diào)函數(shù)的唯一鍵,并將其與回調(diào)函數(shù)一起存入回調(diào)函數(shù)列表中。最后,更新事件對(duì)應(yīng)的回調(diào)函數(shù)列表,并返回一個(gè)包含unsubscribe方法的對(duì)象。
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);
}
取消訂閱指定事件的回調(diào)函數(shù)。
該方法首先獲取指定事件對(duì)應(yīng)的回調(diào)函數(shù)列表,然后在列表中查找指定鍵的回調(diào)函數(shù),并將其從列表中刪除。如果刪除后回調(diào)函數(shù)列表為空,則刪除該事件對(duì)應(yīng)的鍵值對(duì)。
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í)行所有相關(guān)回調(diào)函數(shù)。
該方法首先獲取指定事件對(duì)應(yīng)的回調(diào)函數(shù)列表,如果不存在則返回空數(shù)組。然后,對(duì)于列表中的每個(gè)回調(diào)函數(shù),按照順序執(zhí)行其對(duì)應(yīng)的函數(shù),并將返回值存入一個(gè)數(shù)組中。最后,返回保存了所有返回值的數(shù)組。
完整代碼
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'); // []到此這篇關(guān)于基于JavaScript實(shí)現(xiàn)一個(gè)簡(jiǎn)單的事件觸發(fā)器的文章就介紹到這了,更多相關(guān)JavaScript事件觸發(fā)器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
js獲取當(dāng)前地址 JS獲取當(dāng)前URL的示例代碼
本篇文章主要是對(duì)js獲取當(dāng)前地址 JS獲取當(dāng)前URL的示例代碼進(jìn)行了介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-02-02
Javascript實(shí)現(xiàn)的Map集合工具類(lèi)完整實(shí)例
這篇文章主要介紹了Javascript實(shí)現(xiàn)的Map集合工具類(lèi),以完整實(shí)例形式分析了javascript實(shí)現(xiàn)map集合的構(gòu)造、查找、刪除、判斷等相關(guān)技巧,需要的朋友可以參考下2015-07-07
JS中三目運(yùn)算符和if else的區(qū)別分析與示例
本文是通過(guò)示例詳細(xì)分析了JS中三目運(yùn)算符和if else的區(qū)別,是篇非常不錯(cuò)的文章,這里推薦給大家。2014-11-11
layui 數(shù)據(jù)表格 點(diǎn)擊分頁(yè)按鈕 監(jiān)聽(tīng)事件的實(shí)例
今天小編就為大家分享一篇layui 數(shù)據(jù)表格 點(diǎn)擊分頁(yè)按鈕 監(jiān)聽(tīng)事件的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09
js如何判斷訪問(wèn)是來(lái)自搜索引擎(蜘蛛人)還是直接訪問(wèn)
這篇文章主要介紹了js如何判斷訪問(wèn)是來(lái)自搜索引擎(蜘蛛人)還是直接訪問(wèn),需要的朋友可以參考下2015-09-09
js實(shí)現(xiàn)圖片上傳到服務(wù)器和回顯
這篇文章主要介紹了js實(shí)現(xiàn)圖片上傳到服務(wù)器和回顯,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-01-01
JavaScript使ifram跨域相互訪問(wèn)及與PHP通信的實(shí)例
這篇文章主要介紹了JavaScript使ifram跨域相互訪問(wèn)及與PHP通信的實(shí)例,同時(shí)對(duì)同域間的訪問(wèn)也作了詳細(xì)的演示,需要的朋友可以參考下2016-03-03

