点击事件调用匿名函数如何传参_事件发布/订阅模式的简单实现
這是一種廣泛應用于異步編程的模式,是回調函數的事件化,常常用來解耦業務邏輯。事件的發布者無需關注訂閱的偵聽器如何實現業務邏輯,甚至不用關注有多少個偵聽器存在。數據通過消息的方式可以靈活的傳遞。 ——《深入淺出Nodejs》
是的,了解JS的人都清楚,JS中動輒異步,動輒回調函數,尤其是在DOM中綁定事件。而目前廣泛用于處理異步編程的方法主要有發布/訂閱模式,Promise,async/await。
可是,你只知道傳入回調函數,卻不知道這中間都經歷了什么?你只知道回調函數會在合適的時機被調用,可這又是為什么?為什么他傳入之后可以延遲,而不是立即調用?帶著這些疑問,看完下面這些代碼,相信你會有所收獲。
和觀察者模式的區別
先看圖:
兩種模式都可以用于松散耦合,改進代碼管理和潛在的復用,實際使用的時候,不用糾結與到底是什么模式,重要的明白這個思想。
實現
下面我們一起來寫一個簡易的事件類,用來注冊事件或觸發事件等:
class Event { constructor() { this.callback = {} // 儲存事件 } on () {} // 注冊事件 once () {} // 注冊只能調用一次的事件 emit () {} // 觸發事件 del () {} // 移除指定的回調函數}首先,為什么回調函數可以延遲調用?因為內部可以將回調函數存儲起來,所以我們首先需要一個對象來存放回調函數。
為什么是對象而不是數組?因為你可以注冊很多事件呀,每個事件都可以有一個回調數組,存放一系列的回調函數。所以這里使用對象,可以通過不同的屬性訪問不同的事件隊列。
代碼本身不多,是因為加了詳細的注釋:
/** * 注冊事件 * @params type[String] 事件類型 * @params fn[Function] 回調函數 */on (type, fn) { // 首先判斷,callback對象有沒有該事件的回調數組 if (!this.callback[type]) { // 如果沒有的話就新建一個數組用來存儲type事件的回調函數 this.callback[type] = [] } this.callback[type].push(fn) // 將回調函數fn存入數組 return this // 返回this是為了實現鏈式調用}/** * 觸發事件 * @params type[String] 事件類型 * @params ...params 傳入的參數,不限個數 */emit (type, ...params) { // 遍歷執行對應的回調數組,并傳入參數 this.callback[type].forEach(fn => fn(...params)) return this}/** * 注冊一個只能執行一次的事件 * @params type[String] 事件類型 * @params fn[Function] 回調函數 */once (type, fn) { if (!this.callback[type]) { this.callback[type] = [] } let _this = this // 保存執行環境 // 由于只能執行一次,這里需要做點處理 // 注意該函數是有名字的,因為需要刪除。但名字只在函數內部有效 this.callback[type].push(function once (...args) { fn(...args) // 這里是為了方便emit傳參 _this.del(type, once) // 執行一次后刪除自己 }) return this // 鏈式調用}/** * 刪除對應的回調函數 * @params type[String] 事件類型 * @params fn[Function] 回調函數 */del (type, fn) { // 利用filter刪除數組中 this.callback[type] = this.callback[type].filter(cb => fn !== cb) return this}這樣我們就完成了一個自己的事件管理類,下面寫點代碼測試一下:
let event = new Event()// 新建兩個函數let f1 = function (...args) {console.log('參數', ...args)}let f2 = function () {console.log('執行成功!')}event.once('success', f1) // 注冊f1函數,只能執行一次.on('success', f2) // 注冊f2函數.emit('success', 12, 13) // 觸發success,執行所有回調函數.emit('success') // 這里只會執行f2,f1自動移除了.del('success', f2) // 要刪除函數的話就不能傳入匿名函數了.emit('success', 12) // 沒有反應了,全被移除了最后
怎么樣?其實也不難吧。異步編程是JS的重頭戲,理解它的原理是很有必要的。但是說到原理,我覺得在演示方面,挑出重點代碼就行了。原生方法或者某些成熟的庫一般會對參數等做校驗,還要捕捉錯誤,進行錯誤處理。這樣算下來代碼量是很大的,不是很利于理解原理。原來就應該是清晰明了嘛,懂了原理不就可以自己拓展了。
另外感興趣的朋友可以想一想Promise是怎么實現的?為什么它也可以延遲處理?
傳送門
【JS】5行代碼實現bind函數CSS動畫?教你使用障眼法,打造炫酷充電效果JS動畫?其實很簡單。150行代碼,帶你制作雪花飛舞特效
完整代碼
class Event { constructor() { this.callback = {} // 儲存事件 } on (type, fn) { if (!this.callback[type]) { this.callback[type] = [] } this.callback[type].push(fn) return this } once (type, fn) { if (!this.callback[type]) { this.callback[type] = [] } let _this = this this.callback[type].push(function once (...args) { fn(...args) _this.del(type, once) // 執行一次后刪除自己 }) return this } emit (type, ...params) { this.callback[type].forEach(fn => fn(...params)) return this } del (type, fn) { this.callback[type] = this.callback[type].filter(cb => fn !== cb) return this }}let event = new Event()let f1 = function (...name) { console.log('我的名字是:', ...name)}let f2 = function () { console.log('執行成功!')}event.once('success', f1) // 注冊f1函數,只能執行一次.once('success', f2) // 注冊f1函數,只能執行一次.on('success', f2) // 注冊f2函數.emit('success', 12, 13) // 觸發success,執行所有回調函數.emit('success') // 這里只會執行f2,f1自動移除了.del('success', f2) // 要刪除函數的話就不能傳入匿名函數了.emit('success', 12) // 沒有反應了,全被移除了 與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的点击事件调用匿名函数如何传参_事件发布/订阅模式的简单实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 舒尔特注意力训练表格_星孩注意力总是不集
- 下一篇: python高阶函数filter_pyt