# events-观察者模式

# 前言

events模块是node的核心模块,几乎所有常用的node模块都继承了events模块,比如http、fs等。本文将通过实现一版简单的 EventEmitter 来介绍nodeJS中的事件机制

# EventEmitter

多数 Node.js 核心 API 都是采用惯用的异步事件驱动架构,其中某些类型的对象(称为触发器)会周期性地触发命名事件来调用函数对象(监听器)。例如,一个net.Server对象会在每次有新连接时触发一个事件;一个 fs.ReadStream 会在文件被打开时触发一个事件;一个 stream会在数据可读时触发事件等。

# EventEmitter的简单使用




 















const EventEmitter =require('events');
const util = require('util')
function Person(){
}
util.inherits(Person, EventEmitter); // Person 继承 EventEmitter类 拥有 on、emit、off、once、newListener 等方法
const person = new Person();

let listener1 = ()=>{
    console.log('吃完前洗手')
}

person.on('eat', listener1);
let listener2 = ()=>{
    console.log('吃完前洗手')
}
person.on('eat', listener2);

person.emit('eat'); 

从代码可以看出, Person 继承 EventEmitter类,可以拥有EventEmitter 上的方法,通过 on 方法挂载两个函数 listener1 listener2,最后通过 emit 调用挂载到 eat上的所有方法,实现一个简单的发布订阅。

其实内部的结构很简单,通过 on 方法挂载两个方法的结构如下:




 
  _events = {
    'eat': [listener1, listener2]
  }

emit 方法只是根据 eat 找到对应的数组遍历执行。

# 实现 onemit

在上面我们已经知道了 onemit的基本原理,那我们试一下自己来简单实现一下.




 












function EventEmitter() {
    this._enents = Object.create(null) // _enents存放当前全部订阅
}
EventEmitter.prototype.on = function(eventName, fn) {
    let arr = this._enents[eventName] || (this._enents[eventName] = [])
    arr.push(fn) 
}
EventEmitter.prototype.emit = function(eventName) {
    if (this._enents[eventName]) {
        this._enents[eventName].forEach(fn => {
            fn()
        });
    }
}
module.exports = EventEmitter

但上面代码执行的时候会发现报了一个错




 
let arr = this._enents[eventName] || (this._enents[eventName] = [])
                      ^
TypeError: Cannot read property 'eat' of undefined

经过排查才发现 我们再调用on方法的时候,this指向person实例,但是person实例上面没有 this._enents ,所以报错

那我们再优化下代码,做个边界处理吧




 














function EventEmitter() {
    this._events = Object.create(null)
}
EventEmitter.prototype.on = function(eventName, fn) {
    if (!this._events) {
        this._events = Object.create(null)
    }
    let arr = this._events[eventName] || (this._events[eventName] = [])
    arr.push(fn)
}
EventEmitter.prototype.emit = function(eventName) {
    if (this._events[eventName]) {
        this._events[eventName].forEach(fn => {
            fn()
        });
    }
}

那我们目前就实现了一个最简单的发布订阅了.

# 实现 offnewListeneronce

EventEmitter 上还有其他常见的用法 如 offnewListeneronce, 那我们先来看下他们的用法吧

off: 删除挂载的函数




 











const EventEmitter =require('events');
const util = require('util')
function Person(){
}
util.inherits(Person, EventEmitter);
const person = new Person();

let listener2 = ()=>{
    console.log('吃完前洗手')
}
person.on('eat', listener2);
person.off('eat',listener2);

person.emit('eat'); 

从代码我们可以发现,通过 on 挂载了一个 eat, 但是后面用 off 对他进行删除,所以 emit发布的时候没有打印出任何东西.

newListener: 可以获取到代码中后序调用 on 或者 once 方法中传入的 `




 
















const EventEmitter =require('events');
const util = require('util')
function Person(){
}
util.inherits(Person, EventEmitter);
const person = new Person();

person.on('newListener', (type)=>{
    process.nextTick(()=>{ 
        if(type === 'eat'){
            person.emit('eat');
        }
    })
})

let listener2 = ()=>{
    console.log('吃完前洗手')
}
person.on('eat', listener2);

通过代码可以发现, on 方法如果传入 newListener的话,其回调函数可以接受一个参数 type, type是代码后序使用 on 或者 once 挂载的 eventName,然后来进行逻辑处理

once: 和 on一样是挂载,但是挂载的函数执行之后就会清除




 












const EventEmitter =require('events');
const util = require('util')
function Person(){
}
util.inherits(Person, EventEmitter);
const person = new Person();


let listener2 = ()=>{
    console.log('吃完前洗手')
}
person.once('eat', listener2);

person.emit('eat')
person.emit('eat')

代码的运行结果是只打印一次 '吃完前洗手',表示 once 方法挂载的方法只能被执行一次

那知道大致的用法之后,手也忍不住的自己实现一版了

# off 的实现




 




EventEmitter.prototype.off = function(eventName,fn){
    if( this._events[eventName]){
        this._events[eventName] = this._events[eventName].filter(item=>{
            return  item !== fn && item.l !== fn
        })
    }
}

# newListener 的实现





 








EventEmitter.prototype.on = function(eventName,callback){ 
    if(!this._events){
        this._events = Object.create(null);
    }
    if(eventName !== 'newListener'){
        if(this._events['newListener']){
            this._events['newListener'].forEach(fn=>fn(eventName))
        }
    }
    let arr = this._events[eventName] || (this._events[eventName] = []);
    arr.push(callback)
}

# once 的实现




 





EventEmitter.prototype.once = function(eventName,callback){
    const once = (...args)=>{ // 高阶函数
        callback(...args);// 先执行原有的逻辑
        this.off(eventName,once);// 在将这个函数移除掉
    }
    once.l = callback
    this.on(eventName,once); 
}

# 源码




 








































function EventEmitter(){
    this._events  = Object.create(null);
}
// 订阅 创建关系
EventEmitter.prototype.on = function(eventName,callback){
    if(!this._events){
        this._events = Object.create(null);
    }
    if(eventName !== 'newListener'){
        if(this._events['newListener']){
            this._events['newListener'].forEach(fn=>fn(eventName))
        }
    }
        let arr = this._events[eventName] || (this._events[eventName] = []);
        arr.push(callback)
 
}
EventEmitter.prototype.once = function(eventName,callback){
    const once = (...args)=>{ // 高阶函数
        callback(...args);// 先执行原有的逻辑
        this.off(eventName,once);// 在将这个函数移除掉
    }
    once.l = callback
    this.on(eventName,once); 
}
// 让对应的函数依次执行
EventEmitter.prototype.emit = function(eventName,...args){
    if(!this._events){
        this._events = Object.create(null);
    }
    if( this._events[eventName]){
        this._events[eventName].forEach(fn => fn(...args));
    }
}
EventEmitter.prototype.off = function(eventName,fn){
    if( this._events[eventName]){
        //  this._events[eventName] = [once]
        this._events[eventName] = this._events[eventName].filter(item=>{
            return  item !== fn && item.l !== fn
        })
    }
}
module.exports = EventEmitter;