成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

面試官:說說Node中的EventEmitter? 如何實(shí)現(xiàn)一個(gè)EventEmitter?

開發(fā) 前端
我們了解到,Node采用了事件驅(qū)動機(jī)制,而EventEmitter就是Node實(shí)現(xiàn)事件驅(qū)動的基礎(chǔ)。

[[404630]]

本文轉(zhuǎn)載自微信公眾號「JS每日一題」,作者灰灰。轉(zhuǎn)載本文請聯(lián)系JS每日一題公眾號。

一、是什么

我們了解到,Node采用了事件驅(qū)動機(jī)制,而EventEmitter就是Node實(shí)現(xiàn)事件驅(qū)動的基礎(chǔ)

在EventEmitter的基礎(chǔ)上,Node幾乎所有的模塊都繼承了這個(gè)類,這些模塊擁有了自己的事件,可以綁定/觸發(fā)監(jiān)聽器,實(shí)現(xiàn)了異步操作

Node.js 里面的許多對象都會分發(fā)事件,比如 fs.readStream 對象會在文件被打開的時(shí)候觸發(fā)一個(gè)事件

這些產(chǎn)生事件的對象都是 events.EventEmitter 的實(shí)例,這些對象有一個(gè) eventEmitter.on() 函數(shù),用于將一個(gè)或多個(gè)函數(shù)綁定到命名事件上

二、使用方法

Node的events模塊只提供了一個(gè)EventEmitter類,這個(gè)類實(shí)現(xiàn)了Node異步事件驅(qū)動架構(gòu)的基本模式——觀察者模式

在這種模式中,被觀察者(主體)維護(hù)著一組其他對象派來(注冊)的觀察者,有新的對象對主體感興趣就注冊觀察者,不感興趣就取消訂閱,主體有更新的話就依次通知觀察者們

基本代碼如下所示:

  1. const EventEmitter = require('events'
  2.  
  3. class MyEmitter extends EventEmitter {} 
  4. const myEmitter = new MyEmitter() 
  5.  
  6. function callback() { 
  7.     console.log('觸發(fā)了event事件!'
  8. myEmitter.on('event', callback) 
  9. myEmitter.emit('event'
  10. myEmitter.removeListener('event', callback); 

通過實(shí)例對象的on方法注冊一個(gè)名為event的事件,通過emit方法觸發(fā)該事件,而removeListener用于取消事件的監(jiān)聽

關(guān)于其常見的方法如下:

  • emitter.addListener/on(eventName, listener) :添加類型為 eventName 的監(jiān)聽事件到事件數(shù)組尾部
  • emitter.prependListener(eventName, listener):添加類型為 eventName 的監(jiān)聽事件到事件數(shù)組頭部
  • emitter.emit(eventName[, ...args]):觸發(fā)類型為 eventName 的監(jiān)聽事件
  • emitter.removeListener/off(eventName, listener):移除類型為 eventName 的監(jiān)聽事件
  • emitter.once(eventName, listener):添加類型為 eventName 的監(jiān)聽事件,以后只能執(zhí)行一次并刪除
  • emitter.removeAllListeners([eventName]):移除全部類型為 eventName 的監(jiān)聽事件

三、實(shí)現(xiàn)過程

通過上面的方法了解,EventEmitter是一個(gè)構(gòu)造函數(shù),內(nèi)部存在一個(gè)包含所有事件的對象

  1. class EventEmitter { 
  2.     constructor() { 
  3.         this.events = {}; 
  4.     } 

其中events存放的監(jiān)聽事件的函數(shù)的結(jié)構(gòu)如下:

  1.   "event1": [f1,f2,f3], 
  2.   "event2": [f4,f5], 
  3.   ... 

然后開始一步步實(shí)現(xiàn)實(shí)例方法,首先是emit,第一個(gè)參數(shù)為事件的類型,第二個(gè)參數(shù)開始為觸發(fā)事件函數(shù)的參數(shù),實(shí)現(xiàn)如下:

  1. emit(type, ...args) { 
  2.     this.events[type].forEach((item) => { 
  3.         Reflect.apply(item, this, args); 
  4.     }); 

當(dāng)實(shí)現(xiàn)了emit方法之后,然后實(shí)現(xiàn)on、addListener、prependListener這三個(gè)實(shí)例方法,都是添加事件監(jiān)聽觸發(fā)函數(shù),實(shí)現(xiàn)也是大同小異

  1. on(type, handler) { 
  2.     if (!this.events[type]) { 
  3.         this.events[type] = []; 
  4.     } 
  5.     this.events[type].push(handler); 
  6.  
  7. addListener(type,handler){ 
  8.     this.on(type,handler) 
  9.  
  10. prependListener(type, handler) { 
  11.     if (!this.events[type]) { 
  12.         this.events[type] = []; 
  13.     } 
  14.     this.events[type].unshift(handler); 

緊接著就是實(shí)現(xiàn)事件監(jiān)聽的方法removeListener/on

  1. removeListener(type, handler) { 
  2.     if (!this.events[type]) { 
  3.         return
  4.     } 
  5.     this.events[type] = this.events[type].filter(item => item !== handler); 
  6.  
  7. off(type,handler){ 
  8.     this.removeListener(type,handler) 

最后再來實(shí)現(xiàn)once方法, 再傳入事件監(jiān)聽處理函數(shù)的時(shí)候進(jìn)行封裝,利用閉包的特性維護(hù)當(dāng)前狀態(tài),通過fired屬性值判斷事件函數(shù)是否執(zhí)行過

  1. once(type, handler) { 
  2.     this.on(type, this._onceWrap(type, handler, this)); 
  3.   } 
  4.  
  5.   _onceWrap(type, handler, target) { 
  6.     const state = { fired: false, handler, type , target}; 
  7.     const wrapFn = this._onceWrapper.bind(state); 
  8.     state.wrapFn = wrapFn; 
  9.     return wrapFn; 
  10.   } 
  11.  
  12.   _onceWrapper(...args) { 
  13.     if (!this.fired) { 
  14.       this.fired = true
  15.       Reflect.apply(this.handler, this.target, args); 
  16.       this.target.off(this.type, this.wrapFn); 
  17.     } 
  18.  } 

完整代碼如下:

  1. class EventEmitter { 
  2.     constructor() { 
  3.         this.events = {}; 
  4.     } 
  5.  
  6.     on(type, handler) { 
  7.         if (!this.events[type]) { 
  8.             this.events[type] = []; 
  9.         } 
  10.         this.events[type].push(handler); 
  11.     } 
  12.  
  13.     addListener(type,handler){ 
  14.         this.on(type,handler) 
  15.     } 
  16.  
  17.     prependListener(type, handler) { 
  18.         if (!this.events[type]) { 
  19.             this.events[type] = []; 
  20.         } 
  21.         this.events[type].unshift(handler); 
  22.     } 
  23.  
  24.     removeListener(type, handler) { 
  25.         if (!this.events[type]) { 
  26.             return
  27.         } 
  28.         this.events[type] = this.events[type].filter(item => item !== handler); 
  29.     } 
  30.  
  31.     off(type,handler){ 
  32.         this.removeListener(type,handler) 
  33.     } 
  34.  
  35.     emit(type, ...args) { 
  36.         this.events[type].forEach((item) => { 
  37.             Reflect.apply(item, this, args); 
  38.         }); 
  39.     } 
  40.  
  41.     once(type, handler) { 
  42.         this.on(type, this._onceWrap(type, handler, this)); 
  43.     } 
  44.  
  45.     _onceWrap(type, handler, target) { 
  46.         const state = { fired: false, handler, type , target}; 
  47.         const wrapFn = this._onceWrapper.bind(state); 
  48.         state.wrapFn = wrapFn; 
  49.         return wrapFn; 
  50.     } 
  51.  
  52.     _onceWrapper(...args) { 
  53.         if (!this.fired) { 
  54.             this.fired = true
  55.             Reflect.apply(this.handler, this.target, args); 
  56.             this.target.off(this.type, this.wrapFn); 
  57.         } 
  58.     } 

測試代碼如下:

  1. const ee = new EventEmitter(); 
  2.  
  3. // 注冊所有事件 
  4. ee.once('wakeUp', (name) => { console.log(`${name} 1`); }); 
  5. ee.on('eat', (name) => { console.log(`${name} 2`) }); 
  6. ee.on('eat', (name) => { console.log(`${name} 3`) }); 
  7. const meetingFn = (name) => { console.log(`${name} 4`) }; 
  8. ee.on('work', meetingFn); 
  9. ee.on('work', (name) => { console.log(`${name} 5`) }); 
  10.  
  11. ee.emit('wakeUp''xx'); 
  12. ee.emit('wakeUp''xx');         // 第二次沒有觸發(fā) 
  13. ee.emit('eat''xx'); 
  14. ee.emit('work''xx'); 
  15. ee.off('work', meetingFn);        // 移除事件 
  16. ee.emit('work''xx');           // 再次工作 

參考文獻(xiàn)

 

  • http://nodejs.cn/api/events.html#events_class_eventemitter
  • https://segmentfault.com/a/1190000015762318
  • https://juejin.cn/post/6844903781230968845
  • https://vue3js.cn/interview

 

責(zé)任編輯:武曉燕 來源: JS每日一題
相關(guān)推薦

2022-04-12 08:09:22

Nodejs前端面試題

2024-03-28 10:37:44

IoC依賴注入依賴查找

2024-03-05 10:33:39

AOPSpring編程

2024-08-22 10:39:50

@Async注解代理

2025-04-08 00:00:00

@AsyncSpring異步

2021-05-20 08:34:03

CDN原理網(wǎng)絡(luò)

2024-07-31 08:28:37

DMAIOMMap

2024-03-14 14:56:22

反射Java數(shù)據(jù)庫連接

2024-12-06 07:00:00

2024-03-22 06:56:24

零拷貝技術(shù)數(shù)據(jù)傳輸數(shù)據(jù)拷貝

2024-09-20 08:36:43

零拷貝數(shù)據(jù)傳輸DMA

2024-02-29 16:49:20

volatileJava并發(fā)編程

2024-08-29 16:30:27

2024-08-12 17:36:54

2021-06-07 09:41:48

NodeBuffer 網(wǎng)絡(luò)協(xié)議

2021-06-08 08:33:23

NodeStream數(shù)據(jù)

2021-06-10 07:51:07

Node.js循環(huán)機(jī)制

2024-06-04 09:02:03

2025-02-28 00:00:00

2021-06-07 17:12:22

線程安全Atomic
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 蜜桃av一区二区三区 | 国产精品久久久久aaaa九色 | av日韩高清 | 亚洲人成人一区二区在线观看 | 中文字幕第九页 | 午夜影院在线 | 亚洲欧洲日本国产 | 国产视频精品在线 | 国产精品久久久久久久久久久久午夜片 | 国产中文字幕在线观看 | 欧美亚洲一区二区三区 | 亚洲精品久久久久久一区二区 | 91在线播| 久草成人| 国产区在线免费观看 | 在线观看av免费 | 一区二区三区四区不卡 | 在线高清免费观看视频 | 国产乡下妇女做爰 | 国产一区二区三区久久久久久久久 | 久久亚洲综合 | 毛片黄 | 国产精品国产精品国产专区不卡 | 欧美精品成人影院 | 四虎网站在线观看 | 欧美精品黄 | 精品一区av| 欧美激情精品久久久久久免费 | 欧美精品一二区 | 免费人成激情视频在线观看冫 | 日韩精品视频在线 | 国产91在线播放 | 久久精品国产久精国产 | 日韩在线视频观看 | 国产精品免费小视频 | 国产精品久久久久久久久婷婷 | 欧美国产一区二区三区 | 在线观看日韩精品视频 | 91av免费观看| 中文字幕在线观看视频一区 | 日本欧美国产在线观看 |