摘要:屬性該方法用于存儲的整個事件名稱與回調(diào)函數(shù)的集合,初始值為。當(dāng)該屬性當(dāng)前值為一個對象且其函數(shù)不等于函數(shù)時,則會將其轉(zhuǎn)換為一個包含這兩個事件對象的事件對象數(shù)組。
背景
事件監(jiān)聽在前端的開發(fā)過程中是一個很常見的情況。DOM上的事件監(jiān)聽方式,讓我們看到了通過事件的方式來進(jìn)行具體的業(yè)務(wù)邏輯的處理的便捷。
在具體的一些業(yè)務(wù)場景中,第三方的自定義事件能夠在層級較多,函數(shù)調(diào)用困難以及需要多個地方響應(yīng)的時候有著其獨(dú)特的優(yōu)勢——調(diào)用方便,避免多層嵌套,降低組件間耦合性。
這篇文章所提到的EventEmitter3,就是一個典型的第三方事件庫,能夠讓我們通過自定義的實(shí)踐來實(shí)現(xiàn)多個函數(shù)與組件間的通信。
整體結(jié)構(gòu)圖EventEmitter3的設(shè)計(jì)較為的簡單,具體結(jié)構(gòu)可以看下圖所示。
下面我們將按照一般人的正常思路來對這個結(jié)構(gòu)進(jìn)行介紹。
各部分結(jié)構(gòu)與功能 EEfunction EE(fn, context, once) { this.fn = fn; this.context = context; this.once = once || false; }
從類EE的代碼中我們能夠很明確的了解到,第一個參數(shù)為回調(diào)函數(shù),第二個參數(shù)為回調(diào)函數(shù)的上下文,第三個參數(shù)是一個once的標(biāo)志位。由于代碼簡單,在這里就簡單介紹下了。
Prototype屬性 events該方法用于存儲eventEmitter的整個事件名稱與回調(diào)函數(shù)的集合,初始值為undefined。
Prototype方法 eventName作用:返回當(dāng)前已經(jīng)注冊的事件名稱的列表
參數(shù):無
listeners作用:返回某一個事件名稱的所有監(jiān)聽函數(shù)
參數(shù):event——事件名稱,exists——是否只判斷存在與否
emit作用:觸發(fā)某個事件
參數(shù):event——事件名,a1~a5——參數(shù)1~5
on作用:為某個事件添加一個監(jiān)聽函數(shù)
參數(shù):event——事件名,fn——回調(diào)函數(shù),context——上下文
once作用:類似on,區(qū)別在于該函數(shù)只會觸發(fā)一次
參數(shù):event——事件名,fn——回調(diào)函數(shù),context——上下文
removeListner作用:移除某個事件的監(jiān)聽函數(shù)
參數(shù):event——事件名,fn——事件監(jiān)聽函數(shù),context——只移除上下文匹配的事件監(jiān)聽函數(shù),once——只移除類型匹配的事件監(jiān)聽函數(shù)
removeAllListener作用:移除某個時間的所有監(jiān)聽函數(shù)
參數(shù):event——事件名
學(xué)習(xí)思路下面我們將從添加監(jiān)聽函數(shù), 事件觸發(fā)與刪除監(jiān)聽函數(shù)來進(jìn)行具體的代碼分析,從而了解該庫的實(shí)現(xiàn)思路。
事件對象具體代碼如下所示:
//一個單一的事件監(jiān)聽函數(shù)的單元 // // @param {Function} fn Event handler to be called. 回調(diào)函數(shù) // @param {Mixed} context Context for function execution. 函數(shù)執(zhí)行上下文 // @param {Boolean} [once=false] Only emit once 是否執(zhí)行一次的標(biāo)志位 // @api private 私有API function EE(fn, context, once) { this.fn = fn; this.context = context; this.once = once || false; }
該類為eventEmitter中用于存儲事件監(jiān)聽函數(shù)的最小類。
添加監(jiān)聽函數(shù)on函數(shù)具體代碼如下所示:
// Register a new EventListener for the given event. // 注冊一個指定的事件的事件監(jiān)聽函數(shù) // // @param {String} event Name of the event. 事件名 // @param {Function} fn Callback function. 回調(diào)函數(shù) // @param {Mixed} [context=this] The context of the function. 上下文 // @api public 公有API EventEmitter.prototype.on = function on(event, fn, context) { var listener = new EE(fn, context || this) , evt = prefix ? prefix + event : event; if (!this._events) this._events = prefix ? {} : Object.create(null); if (!this._events[evt]) { this._events[evt] = listener;//第一次存儲為一個事件監(jiān)聽對象 } else { if (!this._events[evt].fn) {//第三次及以后則直接向?qū)ο髷?shù)組中添加事件監(jiān)聽對象 this._events[evt].push(listener); } else {//第二次將存儲的對象與新對象轉(zhuǎn)換為事件監(jiān)聽對象數(shù)組 this._events[evt] = [ this._events[evt], listener ]; } } return this; }
當(dāng)我們向事件E添加函數(shù)F時,會調(diào)用on方法,此時on方法會檢查eventEmitter中prototype屬性events的E屬性。
當(dāng)這個屬性為undefined時,直接將該函數(shù)所在的事件對象賦值給evt屬性。
當(dāng)該屬性當(dāng)前值為一個對象且其函數(shù)fn不等于函數(shù)F時,則會將其轉(zhuǎn)換為一個包含這兩個事件對象的事件對象數(shù)組。
當(dāng)這個屬性已經(jīng)是一個對象數(shù)組時,則直接通過push方法向數(shù)組中添加對象。
prefix是用來判斷Object.create()方法是否存在,如果存在則直接調(diào)用該方法來創(chuàng)建屬性,否則通過在屬性前添加~來避免覆蓋原有屬性。
once的函數(shù)實(shí)現(xiàn)與on函數(shù)基本一致,所以在此就不再進(jìn)行分析。
觸發(fā)監(jiān)聽函數(shù)emit函數(shù)代碼如下所示:
// Emit an event to all registered event listeners. // 觸發(fā)已經(jīng)注冊的事件監(jiān)聽函數(shù) // // @param {String} event The name of the event. 事件名 // @returns {Boolean} Indication if we"ve emitted an event. 如果觸發(fā)事件成功,則返回true,否則返回false // @api public 公有API EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { var evt = prefix ? prefix + event : event; if (!this._events || !this._events[evt]) return false; var listeners = this._events[evt] , len = arguments.length , args , i; if ("function" === typeof listeners.fn) { if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); switch(len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; } for(i = 1, args = new Array(len - 1); i < len; i++) { args[i - 1] = arguments[i]; } listeners.fn.apply(listeners.context, args); } else { //由于篇幅原因省略E屬性為數(shù)組時通過循環(huán)調(diào)用來實(shí)現(xiàn)事件觸發(fā)的過程 } return true; };
當(dāng)我們觸發(fā)事件E時,我們只需要調(diào)用emit方法。該方法會自動檢索事件E中所有的事件監(jiān)聽對象,觸發(fā)所有的事件監(jiān)聽函數(shù),同時移除掉通過once添加,只需要觸發(fā)一次的事件監(jiān)聽函數(shù)。
移除事件監(jiān)聽函數(shù)removeListener函數(shù)代碼如下:
// Remove event listeners. // 移除事件監(jiān)聽函數(shù) // // @param {String} event The event we want to remove. 需要被移除的事件名 // @param {Function} fn The listener that we need to find. 需要被移除的事件監(jiān)聽函數(shù) // @param {Mixed} context Only remove listeners matching this context. 只移除匹配該參數(shù)指定的上下文的監(jiān)聽函數(shù) // @param {Boolean} once Only remove once listeners. 只移除匹配該參數(shù)指定的once屬性的監(jiān)聽函數(shù) // @api public 公共API EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events || !this._events[evt]) return this; var listeners = this._events[evt] , events = []; if (fn) { if (listeners.fn) { if ( listeners.fn !== fn || (once && !listeners.once) || (context && listeners.context !== context) ) { events.push(listeners); } } else { //由于篇幅原因省去便利listeners屬性查找函數(shù)刪除的代碼 } } // // Reset the array, or remove it completely if we have no more listeners. // if (events.length) { this._events[evt] = events.length === 1 ? events[0] : events; } else { delete this._events[evt]; } return this; };
removeListener函數(shù)實(shí)現(xiàn)較為簡單。當(dāng)我們需要移除事件E的某個函數(shù)時,它使用一個event屬性來保存不需要被移除的事件監(jiān)聽對象,然后便利整個事件監(jiān)聽數(shù)組(單個時為對象),并且最后將event屬性的值賦值給E屬性從而覆蓋掉原有的屬性,達(dá)到刪除的目的。
其他該庫中還有一些其他的函數(shù),由于對整個庫的理解不產(chǎn)生太大影響,因此沒有在此進(jìn)行講解,有需要的可以前往我的github倉庫進(jìn)行查看。
缺點(diǎn)eventEmitter的代碼雖然結(jié)構(gòu)清晰,但是仍然存在一些問題。例如on和once方法的實(shí)現(xiàn)中,只有一個屬性不同,其余代碼都一模一樣,其實(shí)可以抽出一個特定的函數(shù)來進(jìn)行處理,通過屬性來進(jìn)行區(qū)分調(diào)用即可。
同時,在同一個函數(shù)例如emit中,也存在大量的重復(fù)代碼,可以進(jìn)行進(jìn)一步的抽象和整理,使得代碼更加簡單。
總結(jié)eventEmitter第三方事件庫從實(shí)現(xiàn)上來看較為簡單,并且結(jié)構(gòu)清晰容易閱讀,推薦有興趣的可以花大約一個小時的時間來學(xué)習(xí)下。
附錄本人github地址——eventEmitter3
官方github地址
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/87759.html
摘要:事件刪除可有可無。創(chuàng)建了一個類,然后在構(gòu)造函數(shù)里初始化一個類的屬性,這個屬性不需要要繼承任何東西,所以用了。但這不是必要的,因?yàn)閷?shí)例化一個都會調(diào)用構(gòu)造函數(shù),皆為初始狀態(tài),應(yīng)該是不可能已經(jīng)定義了的,可去掉。成功執(zhí)行結(jié)束后返回。 GitHub地址:JavaScript EventEmitter博客地址:JavaScript EventEmitter 水平有限,歡迎批評指正 2個多月前把 ...
摘要:批處理的程序分析博客從到學(xué)習(xí)介紹從到學(xué)習(xí)上搭建環(huán)境并構(gòu)建運(yùn)行簡單程序入門從到學(xué)習(xí)配置文件詳解從到學(xué)習(xí)介紹從到學(xué)習(xí)如何自定義從到學(xué)習(xí)介紹從到學(xué)習(xí)如何自定義從到學(xué)習(xí)轉(zhuǎn)換從到學(xué)習(xí)介紹中的從到學(xué)習(xí)中的幾種詳解從到學(xué)習(xí)讀取數(shù)據(jù)寫入到從到學(xué)習(xí)項(xiàng) 批處理的 WordCount 程序分析: https://t.zsxq.com/YJ2Zrfi 博客 1、Flink 從0到1學(xué)習(xí) —— Apache ...
摘要:再如通過處理流數(shù)據(jù)生成簡單的報告,如五分鐘的窗口聚合數(shù)據(jù)平均值。復(fù)雜的事情還有在流數(shù)據(jù)中進(jìn)行數(shù)據(jù)多維度關(guān)聯(lián)聚合塞選,從而找到復(fù)雜事件中的根因。因?yàn)楦鞣N需求,也就造就了現(xiàn)在不斷出現(xiàn)實(shí)時計(jì)算框架,而下文我們將重磅介紹我們推薦的實(shí)時計(jì)算框架。 前言 先廣而告之,本文摘自本人《大數(shù)據(jù)重磅炸彈——實(shí)時計(jì)算框架 Flink》課程第二篇,內(nèi)容首發(fā)自我的知識星球,后面持續(xù)在星球里更新,這里做個預(yù)告,今...
摘要:背景在工作中雖然我經(jīng)常使用到庫但是很多時候?qū)Φ囊恍└拍钸€是處于知其然不知其所以然的狀態(tài)因此就萌生了學(xué)習(xí)源碼的想法剛開始看源碼的時候自然是比較痛苦的主要原因有兩個第一網(wǎng)上沒有找到讓我滿意的詳盡的源碼分析的教程第二我也是第一次系統(tǒng)地學(xué)習(xí)這么大代 背景 在工作中, 雖然我經(jīng)常使用到 Netty 庫, 但是很多時候?qū)?Netty 的一些概念還是處于知其然, 不知其所以然的狀態(tài), 因此就萌生了學(xué)...
摘要:引言給迷失在如何學(xué)習(xí)區(qū)塊鏈技術(shù)的同學(xué)一個指引,區(qū)塊鏈技術(shù)是隨比特幣誕生,因此要搞明白區(qū)塊鏈技術(shù),應(yīng)該先了解下比特幣。但區(qū)塊鏈技術(shù)不單應(yīng)用于比特幣,還有非常多的現(xiàn)實(shí)應(yīng)用場景,想做區(qū)塊鏈應(yīng)用開發(fā),可進(jìn)一步閱讀以太坊系列。 本文始發(fā)于深入淺出區(qū)塊鏈社區(qū), 原文:區(qū)塊鏈技術(shù)學(xué)習(xí)指引 原文已更新,請讀者前往原文閱讀 本章的文章越來越多,本文是一個索引帖,方便找到自己感興趣的文章,你也可以使用左側(cè)...
閱讀 2333·2021-11-24 10:33
閱讀 1396·2019-08-30 15:43
閱讀 3289·2019-08-29 17:24
閱讀 3496·2019-08-29 14:21
閱讀 2235·2019-08-29 13:59
閱讀 1749·2019-08-29 11:12
閱讀 2821·2019-08-28 18:00
閱讀 1865·2019-08-26 12:17