node 之EventEmitter實現

渣渣的生存之道發表於2019-02-22

node 根據事件驅動,那基本都是回撥,最常用的叫釋出訂閱模式,什麼叫釋出訂閱呢?對比觀察者模式,前者是主動地,後者是被動的

//釋出訂閱模式
//將時間放到陣列中,當真的發生時在執行

//let  EventEmitter = require(`events`);
let  EventEmitter = require(`./EventEmitter`);
let {inherits} = require(`util`);
function Girl(){}
inherits(Girl,EventEmitter);
let girl = new Girl;
let cry = () =>{
    console.log(`cry`)
}
let drink = () =>{
    console.log(`drink`)
}
girl.on(`one`,cry)
girl.on(`two`,drink)

girl.emit(`two`)
複製程式碼

可以正常輸出1.txt裡面的內容,那麼EventEmitter是如何實現的呢?

EventEmitter實現

EventEmitter骨架

function EventEmitter(){

}
EventEmitter.prototype.on = function(type,callback){

}
EventEmitter.prototype.emit = function(type){
    
}
module.exports = EventEmitter;
複製程式碼

EventEmitter.on

function EventEmitter(){
    this._events = {};
}
//this._events = {one:[cry],two:[drink]}

EventEmitter.prototype.on = function(type,callback){
    //如果例項不存在則會建立一個空物件
    if(!this._events)  this._events = Object.create(null) //繼承會找不到event ,直接例項可以, Cannot read property `one` of undefined 初始化this._events
    // this._events ={}  和 this._events = Object.create(null) 的區別 前者原型指標指向object鏈,後者沒有任何屬性,後者可以避免第三方繼承
    if(this._events[type]){ // 檢視事件名稱是否存在
        this._events[type].push(callback)
    }else{
        this._events[type] = [callback] //將事件繫結在物件上
    }
}
複製程式碼

**EventEmitter.newListener || EventEmitter.addListener **

EventEmitter.addListener =  EventEmitter.prototype.on = function(type,callback){
    //如果例項不存在則會建立一個空物件
    if(!this._events)  this._events = Object.create(null)
    if(this.newListener && this._events[`newListener`] && this._events[`newListener`].length>0){
        if(type != `newListener`){
            this._events[`newListener`].forEach(fn => fn(type))
        }
    }//繼承會找不到event ,直接例項可以, Cannot read property `one` of undefined 初始化this._events
    // this._events ={}  和 this._events = Object.create(null) 的區別 前者原型指標指向object鏈,後者沒有任何屬性,後者可以避免第三方繼承
    if(this._events[type]){ // 檢視事件名稱是否存在
        this._events[type].push(callback)
    }else{
        this._events[type] = [callback] //將事件繫結在物件上
    }
}
複製程式碼

EventEmitter.defaultMaxListeners || emitter.getMaxListeners() || emitter.setMaxListeners()

EventEmitter.defaultMaxListeners = 10; //預設最大監聽數
EventEmitter.prototype.setMaxListeners = function(count){
    this._count = count;
}
EventEmitter.prototype.getMaxListeners = function(){
    return  this._count || EventEmitter.defaultMaxListeners
}
EventEmitter.addListener = EventEmitter.prototype.on = function(type,callback){
    ...
    if(this._events[type].length === this.getMaxListeners()){
        console.warn(`memeoy link detected`)
    }
}
複製程式碼

EventEmitter.eventNames || listeners

EventEmitter.prototype.eventNames = function(){
    return Object.keys(this._events)
}
EventEmitter.prototype.listeners = function(type){
    return this._events[type]
}
複製程式碼

removeListener || removeAllListeners

//找到陣列裡的方法對應的移除掉即可
EventEmitter.prototype.removeListener = function(type,callback){
    if (typeof callback !== `function`)  throw new TypeError(`"callback" argument must be a function`);
    if(this._events[type]){
        // filter()把傳入的函式依次作用於每個元素,然後根據返回值是true還是false決定保留還是丟棄該元素。
        //this._events[type] = this._events[type].filter(fn => fn != callback)
        //一次只刪除一個,不是刪除所有相同的type,應該用下面方法
        // for(let i = 0;i < this._events[type].length;i++){
        //     if(this._events[type][i] == callback){
        //         this._events[type].splice[i]
        //         break;
        //     }
        // }
        //同上效果
        this._events[type].splice(this._events[type].indexOf(callback),1)
    }
}
EventEmitter.prototype.removeAllListeners = function(type){
    if(this._events[type]){
        return this._events[type] = Obj.create(null)
    }else{

    }
}
複製程式碼

EventEmitter.once

EventEmitter.prototype.once = function (type,callback){
    //當emit時warp執行需要繼續將引數傳遞給callback
    let warp = (...args) =>{
        callback(...args);
        this.removeListener(type,warp) //用on繫結的時候,在emit的時候執行wap
    }
    warp.l = callback;//將callback方法掛在到warp屬性上
    this.on(type,warp);
}
//在once包了一層wap,我們需要對此wap做細節處理

複製程式碼

emit

EventEmitter.prototype.emit = function(type , ...args){
    if(this._events[type]){
        this._events[type].forEach(fn => fn(...args));
    }else{
        this._events = {}
    }
}
//找到陣列裡的方法對應的移除掉即可
EventEmitter.prototype.removeListener = function(type,callback){
    if (typeof callback !== `function`)  throw new TypeError(`"callback" argument must be a function`);
    if(this._events[type]){
        // filter()把傳入的函式依次作用於每個元素,然後根據返回值是true還是false決定保留還是丟棄該元素。
        //this._events[type] = this._events[type].filter(fn =>{
        //     return fn != callback && fn.l != callback
        // })
        //一次只刪除一個,不是刪除所有相同的type,應該用下面方法
        for(let i = 0;i < this._events[type].length;i++){
            if(this._events[type][i] == callback || this._events[type][i].l == callback ){
                this._events[type].splice[i]
                break;
            }
        }
        //不考慮warp同上效果
        //this._events[type].splice(this._events[type].indexOf(callback) ,1)
    }
}
複製程式碼

所以現在我們寫出的程式碼總結如下

function EventEmitter(){
    this._events = {}
}
//this._events = {one:[cry],two:[drink]}
EventEmitter.prototype.eventNames = function(){
    return Object.keys(this._events)
}
EventEmitter.prototype.listeners = function(type){
    return this._events[type]
}

EventEmitter.defaultMaxListeners = 10; //預設最大監聽數
EventEmitter.prototype.setMaxListeners = function(count){
    this._count = count;
}
EventEmitter.prototype.getMaxListeners = function(){
    return  this._count || EventEmitter.defaultMaxListeners
}

EventEmitter.addListener = EventEmitter.prototype.on = function(type,callback){
    //如果例項不存在則會建立一個空物件
    if(!this._events)  this._events = Object.create(null) //繼承會找不到event ,直接例項可以, Cannot read property `one` of undefined 初始化this._events
    // this._events ={}  和 this._events = Object.create(null) 的區別 前者原型指標指向object鏈,後者沒有任何屬性,後者可以避免第三方繼承
    //如果當前不是newListenernewListener方法就需要讓newListener回撥一次執行,傳入類
    if(this.newListener && this._events[`newListener`] && this._events[`newListener`].length>0){
        if(type != `newListener`){
            this._events[`newListener`].forEach(fn => fn(type))
        }
    }
    if(this._events[type]){ // 檢視事件名稱是否存在
        this._events[type].push(callback)
    }else{
        this._events[type] = [callback] //將事件繫結在物件上
    }
    if(this._events[type].length === this.getMaxListeners()){
        console.warn(`memeoy link detected`)
    }
}

EventEmitter.prototype.emit = function(type , ...args){
    if(this._events[type]){
        this._events[type].forEach(fn => fn(...args));
    }else{
        this._events = {}
    }
}
//找到陣列裡的方法對應的移除掉即可
EventEmitter.prototype.removeListener = function(type,callback){
    if (typeof callback !== `function`)  throw new TypeError(`"callback" argument must be a function`);
    if(this._events[type]){
        // filter()把傳入的函式依次作用於每個元素,然後根據返回值是true還是false決定保留還是丟棄該元素。
        //this._events[type] = this._events[type].filter(fn =>{
        //     fn != callback && fn.l != callback
        // })
        //一次只刪除一個,不是刪除所有相同的type,應該用下面方法
        for(let i = 0;i < this._events[type].length;i++){
            if(this._events[type][i] == callback || this._events[type][i].l == callback ){
                this._events[type].splice[i]
                break;
            }
        }
        //不考慮warp同上效果
        //this._events[type].splice(this._events[type].indexOf(callback) ,1)
    }
}
EventEmitter.prototype.removeAllListeners = function(type){
    if(this._events[type]){
        return this._events[type] = Obj.create(null)
    }else{

    }
}
EventEmitter.prototype.once = function (type,callback){
    //當emit時warp執行需要繼續將引數傳遞給callback
    let warp = (...args) =>{
        callback(...args);
        this.removeListener(type,warp) //用on繫結的時候,在emit的時候執行wap
    }
    warp.l = callback;//將callback方法掛在到warp屬性上
    this.on(type,warp);
}

module.exports = EventEmitter;
複製程式碼

測試程式碼

//釋出訂閱模式
//將時間放到陣列中,當真的發生時在執行

// let  EventEmitter = require(`events`);
let  EventEmitter = require(`./EventEmitter`);
let {inherits} = require(`util`);
function Girl(){
}
inherits(Girl,EventEmitter);
let girl = new Girl;
let cry = (a,b,c) =>{
    console.log(`cry`,a,b,c)
}
let drink = () =>{
    console.log(`drink`)
}

girl.on(`newListener`,(type) => {
    console.log(type)
})
girl.on(`one`,cry)
girl.on(`one`,drink)
girl.on(`one`,cry)


girl.removeListener(`one`,cry) //找到one把 事件刪掉

girl.emit(`one`,1,2,3);
console.log(girl.eventNames())
console.log(girl.listeners(`one`))
複製程式碼

util使用

我們經常用的util模組(node 8版本以上)

let {promisify,inherits} = require(`util`);
let fs = require(`fs`);

let read = promisify(fs.readFile); //
read(`1.txt`,`utf8`).then(data =>{
    console.log(data)
})
//相當於
async function r(){
    try{  
        let result = await read(`1.txt`,`utf8`)
        return result;
    }catch(e){
        throw e;
    }
}
r().then(data=>{
    console.log(data)
},err=>{console.log(err)})
//koa 後期也是採用 async await

function A(){

}
A.prototype.a = 1;
function B(){

}
inherits(B,A)//b繼承a 之繼承公有方法
let b = new B()
console.log(b.a);
//相當於
B.prototype = Object.create(A.prototype)
//相當於
B.prototype.__proto__ = A.prototype
//相當於
Object.setPrototypeOf(B.prototype,A.prototype)
複製程式碼

相關文章