摘要:微型庫解讀之的前端起源關(guān)于我想應(yīng)該很多同學(xué)都很熟悉了。小而美的整個庫非常的小,但是卻功能齊全,為了縮減代碼,也是有一些小技巧在里面。這幾天也在瘋狂的看寫的一些庫,他的庫都有小而美的特點,無論是著名的還是簡單的這種庫。
title: 微型庫解讀之200byte的EventEmitter - Mitt
tags: 前端
關(guān)于 EventEmitter 我想應(yīng)該很多同學(xué)都很熟悉了。簡而言之是一個事件的發(fā)布與訂閱器。
這兩天讀到了一些非常有意思的小庫,雖然小但是功能完備,比如說這次我們要講解的 Mitt.
Github地址
小Mitt是一個微型的 EventEmitter 庫,實現(xiàn)了基本的 on, off, emit 三個Api,對于使用 EventEmitter 其他功能不多的同學(xué)來說,200byte 的體積可以說是非常劃算了。
當然小也有其付出的代價,那就是只支持這三個功能。
至于怎么取舍,見仁見智吧,我建議是先使用 mitt,就算后期要更換別的庫,因為 Api 統(tǒng)一,所以更換起來基本不費事。
Mitt 在 Github的 demo 中,也顯示出了代碼雖小,五臟俱全的特點。
Demo:
import mitt from "mitt" let emitter = mitt() // listen to an event emitter.on("foo", e => console.log("foo", e) ) // listen to all events emitter.on("*", (type, e) => console.log(type, e) ) // fire an event emitter.emit("foo", { a: "b" }) // working with handler references: function onFoo() {} emitter.on("foo", onFoo) // listen emitter.off("foo", onFoo) // unlisten代碼解讀
在研究 Mitt 能完成的功能后,也在想為什么能做到這么小。
在這兒對一些閃光點做一些解讀。
export default function mitt(all: EventHandlerMap) { all = all || Object.create(null); return { // ...Api } }
在初始化 mitt 時,會有一個可選的參數(shù) all,用于存放要監(jiān)聽的事件。
如果初始化不傳參時,會使用 Object.create(null) 來實現(xiàn)。
這樣的好處在于,生成的對象是一個原型為空的對象。
優(yōu)點如下:
節(jié)約內(nèi)存
避免沖突
節(jié)約內(nèi)存是因為沒有了原型,可以節(jié)省部分開銷。
避免沖突則是因為在普通對象中,當要觸發(fā)的事件與對象原型上的屬性或方法重名時,會出現(xiàn)事件不存在卻被錯誤觸發(fā)導(dǎo)致不必要的問題。
var obj = {}; console.log("toString" in obj); var noPrototypeObj = Object.create(null); console.log("toString" in noPrototypeObj);
輸出結(jié)果如下:
簡潔的隊列初始化經(jīng)常我們會做這樣一個操作,當對象中某個屬性不存在時,就初始化,存在則直接返回值。用代碼表示就是:
var obj = {}; var getQueue = (key) => { if (!obj[key]) { obj[key] = [] } return obj[key] }
這是一個很常見的操作,但是在 mitt 的卻簡潔了很多。
export default function mitt(all: EventHandlerMap) { all = all || Object.create(null); return { /** * Register an event handler for the given type. * * @param {String} type Type of event to listen for, or `"*"` for all events * @param {Function} handler Function to call in response to given event * @memberOf mitt */ on(type: string, handler: EventHandler) { (all[type] || (all[type] = [])).push(handler); } }; }
在 on 函數(shù)之中,有這么一句:(all[type] || (all[type] = []))
這個表達式的意思很簡單,有值取值,無值初始化。
但是總的代碼量比起之前的代碼小了很多,實現(xiàn)了簡化代碼的目的。
PS:這個操作我之前在讀 React setState 源代碼時,也碰到過。
其中 queue 的獲取便是使用了這種方式。
無符號右移(>>>)在 off 的Api中,有使用到無符號右移(>>>)的操作,具體操作如下:
/** * Remove an event handler for the given type. * * @param {String} type Type of event to unregister `handler` from, or `"*"` * @param {Function} handler Handler function to remove * @memberOf mitt */ off(type: string, handler: EventHandler) { if (all[type]) { all[type].splice(all[type].indexOf(handler) >>> 0, 1); } }
其中 all[type].splice(all[type].indexOf(handler) >>> 0, 1); 這一句的作用可謂亮眼。
移除某個指定的事件監(jiān)聽是很正常的事,但可能會有一個問題,就是傳入的要移除的監(jiān)聽器并不存在。
換做以往的代碼,可能你會先搜索,再決定是否執(zhí)行移除操作,但是這樣一來代碼量就又增加了。
而無符號右移(>>>),恰恰符合我們的需要。
具體的作用如 demo,在搜索的事件監(jiān)聽函數(shù)不存在時,會返回一個極大的正數(shù),傳入 splice 后,并不會刪除已有的函數(shù)監(jiān)聽器,從而實現(xiàn)了想要的功能。
監(jiān)聽所有事件的監(jiān)聽器在日常開發(fā)中,經(jīng)??赡芟氡O(jiān)聽所有的事件,來輔助開發(fā)。
而 mitt 就實現(xiàn)了這個功能。
Demo:
import mitt from "mitt" let emitter = mitt() // listen to all events emitter.on("*", (type, e) => console.log(type, e) )
而在源代碼里,這個的實現(xiàn)很簡潔:
/** * Invoke all handlers for the given type. * If present, `"*"` handlers are invoked after type-matched handlers. * * @param {String} type The event type to invoke * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler * @memberOf mitt */ emit(type: string, evt: any) { (all[type] || []).slice().map((handler) => { handler(evt); }); (all["*"] || []).slice().map((handler) => { handler(type, evt); }); }
就是在使用 emit 函數(shù)時,找出事件類型為 * 的監(jiān)聽器,并觸發(fā)它。
小而美Mitt的整個庫非常的小,但是卻功能齊全,為了縮減代碼,也是有一些小技巧在里面。
但是 Mitt的庫小也有缺點,比如參數(shù)的類型如果傳錯了,它并不會預(yù)先提示你,這也算是一個要取舍的點吧。
這幾天也在瘋狂的看 developit 寫的一些庫,他的庫都有小而美的特點,無論是著名的 Preact 還是簡單的 mitt 這種庫。他寫的代碼,還是挺值得一讀的。
計劃之后的計劃,可能也準備寫幾篇這種微型庫的源碼閱讀文章,這種庫讀起來輕松,適合每天讀一兩個,而能學(xué)到的東西和思路也不少。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/92794.html
摘要:以下全是別人的源碼,會標明出處,非常感謝原作者,非常感謝開源。優(yōu)雅源碼,微型庫解讀之的源碼零散短路運算符詳情生成的對象是一個原型為空的對象。簡潔的隊列賦值短路邏輯判斷初始化更新數(shù)組,簡直不要太優(yōu)雅。 寧靜致遠。 說明 知識在不斷迭代,除了學(xué)習(xí)資料,那就是學(xué)習(xí)他人,來豐富自己。 以下全是別人的源碼,會標明出處,非常感謝原作者,非常感謝開源。 優(yōu)雅 mitt:源碼,微型庫解讀之20...
摘要:我卻這樣認為我好像是在海上玩耍,時而發(fā)現(xiàn)了一個光滑的石子兒,時而發(fā)現(xiàn)一個美麗的貝殼而為之高興的孩子。在年底之前,挑一些精致美麗的貝殼,分享出來。這是快速,易于使用,讓您擔(dān)心比建立一個文件上傳更重要的問題。 showImg(https://segmentfault.com/img/remote/1460000012622184?w=800&h=534); 我不知道世上的人對我怎樣評價。我...
摘要:預(yù)備工作序最近正在研究相關(guān)的知識,想著如何能自己實現(xiàn)協(xié)議。監(jiān)聽事件就是協(xié)議的抽象,直接在上面監(jiān)聽已有的事件和事件這兩個事件。表示當前數(shù)據(jù)幀為消息的最后一個數(shù)據(jù)幀,此時接收方已經(jīng)收到完整的消息,可以對消息進行處理。 A、預(yù)備工作 1、序 最近正在研究 Websocket 相關(guān)的知識,想著如何能自己實現(xiàn) Websocket 協(xié)議。到網(wǎng)上搜羅了一番資料后用 Node.js 實現(xiàn)該協(xié)議,倒也沒...
摘要:一中的對象包含了錯誤的具體信息,包括錯誤堆棧等。不源碼了,特別簡單,自己去一下。 一. Error ????JS 中的 Error 對象. 包含了錯誤的具體信息,包括 name、message、錯誤堆棧 stack 等??梢砸?new Error 方式創(chuàng)建實例拋出,或調(diào)用 Error.captureStackTrace 為已有對象添加 stack 錯誤堆棧信息 而后拋出showImg(...
摘要:的介紹一般是這樣在中,類是隨內(nèi)核一起發(fā)布的核心庫。庫為帶來了一種存儲原始數(shù)據(jù)的方法,可以讓處理二進制數(shù)據(jù),每當需要在中處理操作中移動的數(shù)據(jù)時,就有可能使用庫。這樣傳遞數(shù)據(jù)會更快。 零、開始之前 1、 首先解釋一下node.js是什么? 2、node.js和javascript有什么不同? 1)因為javascript主要是用在browser,而node.js是在server或者你的電腦...
閱讀 1936·2023-04-25 23:28
閱讀 670·2023-04-25 22:49
閱讀 2410·2021-09-27 13:34
閱讀 5412·2021-09-22 15:09
閱讀 3670·2019-08-30 12:52
閱讀 2802·2019-08-29 15:26
閱讀 709·2019-08-29 11:12
閱讀 2253·2019-08-26 12:24