深入理解 Node.js 中 EventEmitter源码分析(3.0.0版本)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解 Node.js 中 EventEmitter源码分析(3.0.0版本)相关的知识,希望对你有一定的参考价值。
events模块对外提供了一个 EventEmitter 对象,即:events.EventEmitter. EventEmitter 是NodeJS的核心模块events中的类,用于对NodeJS中的事件进行统一管理,使用events可以对特定的API事件进行添加,触发和移除等。
我们可以通过 require(\'events\')来访问该模块。
比如如下代码:
// 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter);
我们先把如上代码放入 main.js 里面,然后在 项目中对应目录下 执行 node main.js 执行结果如下:
如上图可以看到,console.log(events); 打印后,有 EventEmitter 属性,defaultMaxListeners 属性(getter/setter) 方法,init函数属性,及 listenerCount 函数属性等。
1. defaultMaxListeners的含义是:默认事件最大监听个数,在源码中 默认是10个,设置最大监听个数为10个,因为如果监听的个数过多的话,会导致 内存泄露的问题产生。因此默认设置了十个。当然我们在源码中可以设置或者获取最大的监听个数,我们可以设置最大监听的个数 如下代码:
// Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { if (typeof n !== \'number\' || n < 0 || NumberIsNaN(n)) { throw new RangeError(\'The value of "n" is out of range. It must be a non-negative number. Received \' + n + \'.\'); } this._maxListeners = n; return this; };
当然有设置监听的个数,我们也有获取最大的监听个数,如下源码:
function $getMaxListeners(that) { if (that._maxListeners === undefined) return EventEmitter.defaultMaxListeners; return that._maxListeners; } EventEmitter.prototype.getMaxListeners = function getMaxListeners() { return $getMaxListeners(this); };
当然源码中也使用了 Object.defineProperty方法监听该属性值是否发生改变,如下基本源码:
Object.defineProperty(EventEmitter, \'defaultMaxListeners\', { enumerable: true, get: function() { return defaultMaxListeners; }, set: function(arg) { if (typeof arg !== \'number\' || arg < 0 || NumberIsNaN(arg)) { throw new RangeError(\'The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received \' + arg + \'.\'); } defaultMaxListeners = arg; } });
如果defaultMaxListeners值发生改变的话,就会调用相对应的getter/setter方法。
2. events 中的init函数中基本源码如下:
EventEmitter.init = function() { if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) { this._events = Object.create(null); this._eventsCount = 0; } this._maxListeners = this._maxListeners || undefined; };
如上基本代码:this._events 的含义是 保存所有的事件对象,事件的触发和事件的移除操作监听等都在
这个对象_events的基础上实现。
this._eventsCount 的含义是:用于统计事件的个数,也就是_events对象有多少个属性。
this._maxListeners 的含义是:保存最大的监听数的含义。
3. event中的listenerCount函数的作用是:返回指定事件的监听器数量。
如下基本代码:
EventEmitter.listenerCount = function(emitter, type) { if (typeof emitter.listenerCount === \'function\') { return emitter.listenerCount(type); } else { return listenerCount.call(emitter, type); } }; EventEmitter.prototype.listenerCount = listenerCount; function listenerCount(type) { var events = this._events; if (events !== undefined) { var evlistener = events[type]; if (typeof evlistener === \'function\') { return 1; } else if (evlistener !== undefined) { return evlistener.length; } } return 0; }
4. events中的EventEmitter属性
该属性就是events对外提供的一个对象类,使用 EventEmitter 类就是对事件的触发和监听进行封装。
现在我们看下创建 eventEmitter 对象,如下代码吧:
// 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter);
打印信息如下:
可以看到该实列有 _events属性及_maxListeners属性,及该实列上的原型有很多对应的方法,比如 addListener, emit,listenerCount, listeners, on, once, removeAllListeners, removeListener, setMaxListeners 等方法,及_events 及 _maxListeners 等属性。我们先来看下一些简单实列上的属性的基本源码如下:
// Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; EventEmitter.prototype._eventsCount = 0; EventEmitter.prototype._maxListeners = undefined;
一:EventEmitter基本的API使用:
1. addListener(event, listener) 为指定的事件注册一个监听器。该方法是on的别名。该方法接收一个事件名和一个回调函数。
如下代码演示:
// 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter); // 注册 kongzhi 事件 eventEmitter.on(\'kongzhi\', function() { console.log(\'dddd\'); // 打印 dddd }); // 触发kongzhi事件 eventEmitter.emit(\'kongzhi\');
2. listenerCount(eventName)
该方法返回注册了指定事件的监听数量。基本语法如下:
EventEmitter.listenerCount(eventName);
eventName: 指监听的事件名
如下基本代码:
// 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter); // 注册 kongzhi 事件 eventEmitter.on(\'kongzhi\', function() { console.log(\'dddd1111\'); }); // 注册 kongzhi 事件 eventEmitter.on(\'kongzhi\', function() { console.log(\'dddd22222\'); }); // 触发kongzhi事件 eventEmitter.emit(\'kongzhi\'); const num = eventEmitter.listenerCount(\'kongzhi\'); console.log(num); // 返回2 说明注册了两个 kongzhi 这个事件
比如源码中如下代码所示:
EventEmitter.prototype.listenerCount = listenerCount; function listenerCount(type) { var events = this._events; if (events !== undefined) { var evlistener = events[type]; if (typeof evlistener === \'function\') { return 1; } else if (evlistener !== undefined) { return evlistener.length; } } return 0; }
如上代码,先判断 this._events 是否保存了事件,如果保存了事件的话,如果它是个函数的话,那么 return 1; 否则的话,如果不等于undefined,直接返回该监听事件名的长度。其他的情况下 返回 0;
因此上面我们通过如下代码,就可以获取到该监听的事件的长度了:如下代码:
const num = eventEmitter.listenerCount(\'kongzhi\'); console.log(num); // 返回2 说明注册了两个 kongzhi 这个事件
3. listeners(event)
该方法返回指定事件的监听器数组。
如下代码演示:
// 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter); // 注册 kongzhi 事件 eventEmitter.on(\'kongzhi\', function() { console.log(\'dddd1111\'); }); // 注册 kongzhi 事件 eventEmitter.on(\'kongzhi\', function() { console.log(\'dddd22222\'); }); // 触发kongzhi事件 eventEmitter.emit(\'kongzhi\'); const num = eventEmitter.listeners(\'kongzhi\'); console.log(num); // 返回监听事件的数组 num.forEach((func) => { func(); // 我们可以这样指定任何一个事件调用,然后会分别打印 dddd1111, dddd2222 });
4. once(event, listener)
该函数的含义是:为指定事件注册一个单次监听器,也就是说监听器最多只会触发一次,触发后立刻解除该监听器。
如下代码:
// 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter); // 使用on注册监听器 eventEmitter.on(\'kongzhi\', function() { console.log(\'我on事件触发了多少次呢?\'); }); // 注册 kongzhi 事件 eventEmitter.once(\'kongzhi\', function() { console.log(\'我once事件触发了多少次呢?\'); }); // 触发kongzhi事件 eventEmitter.emit(\'kongzhi\'); eventEmitter.emit(\'kongzhi\');
如上代码所示:使用on 事件注册的话,如果使用emit触发的话,触发了多少次,就执行多少次,使用once注册事件的话,emit触发多次的话,最后也只能调用一次,如下执行结果如下:
5. removeListener(eventName, listener)
该方法的作用是:移除指定事件的某个监听器。监听器必须是该事件已经注册过的监听器。
eventName: 事件名称
listener: 回调函数的名称
如下代码演示:
// 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter); const callback = function() { console.log(11111); }; // 使用on注册监听器 eventEmitter.on(\'kongzhi\', callback); // 触发事件 kongzhi eventEmitter.emit(\'kongzhi\'); eventEmitter.removeListener(\'kongzhi\', callback); /* 我们继续触发事件 kongzhi 是不会触发的,因为上面已经使用 removeListener 已经删除了 kongzhi 事件了 */ eventEmitter.emit(\'kongzhi\');
6. removeAllListeners([event])
移除所有事件的所有监听器,如果我们指定事件的话,则是移除指定事件的所有监听器。
参数event: 该参数的含义是事件名称,如果指定了该事件名称的话,则会删除该指定的事件名称对应的所有函数,如果没有指定任何事件名的话,则是删除所有的事件。如下代码所示:
// 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter); const callback = function() { console.log(11111); }; // 使用on注册监听器 eventEmitter.on(\'kongzhi\', callback); // 注册kongzhi2事件 eventEmitter.on(\'kongzhi2\', callback); // 触发事件 kongzhi 是可以触发的 eventEmitter.emit(\'kongzhi\'); // 删除所有的监听器 eventEmitter.removeAllListeners(); /* 我们继续触发事件 kongzhi 和 kongzhi2 是不会触发的,因为上面已经使用 removeAllListeners 已经删除了 所有的 事件了 */ eventEmitter.emit(\'kongzhi\'); eventEmitter.emit(\'kongzhi2\');
7. setMaxListeners(n)
该方法的作用是 设置监听器的默认数量。因为EventEmitters默认的监听器为10个,如果超过10个就会输出警告信息,因此使用该方法,可以设置监听器的默认数量, 使之最大的数量不会报错。
如下代码所示:
// 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter); const callback = function() { console.log(11111); }; // 设置默认监听数量最多为1个,如果超过该数量,控制台会发出警告 eventEmitter.setMaxListeners(1); // 使用on注册监听器 eventEmitter.on(\'kongzhi\', callback); // 注册kongzhi事件 eventEmitter.on(\'kongzhi\', callback); // 触发事件 kongzhi eventEmitter.emit(\'kongzhi\');
执行结果如下所示:
当我们把上面的eventEmitter.setMaxListeners(2); 设置为大于1的时候,就不会报错了,比如最大设置的默认数量为2,监听器最大为2,就不会报错了。
如下图所示:
二:EventEmitter 源码分析
EventEmitter类它实质是一个观察者模式的实现,什么是观察者模式呢?观察者模式定义了对象间的一种一对多的关系,它可以让多个观察者对象同时监听一个主题对象,当一个主题对象发生改变时,所有依赖于它的对象都会得到一个通知。
那么在观察者模式中最典型的实列demo就是 EventEmitter类中on和emit方法,我们可以通过on注册多个事件进行监听,而我们可以通过emit方法来对on事件进行触发。比如如下demo代码:
// 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); eventEmitter.on(\'kongzhi\', function(name) { console.log(\'hello\', name); // 输出:hello 我叫空智 }); eventEmitter.emit(\'kongzhi\', \'我叫空智\');
如上代码,我们通过eventEmitter的emit方法,发出 kongzhi 事件,然后我们通过 eventEmitter 的on方法进行监听,从而执行相对应的函数,从而打印出信息出来。其中on方法属于多个观察者那个对象,它可以有多个on方法进行监听 emit中触发的那个主题对象,当那个emit触发的主体对象发生改变时,所有的on方法监听的对象都会触发。
上面我们知道了 EventEmitter模块的on和emit的用途后,我们首先来实现一个包含 emit和on方法的EventEmitter类。
如下代码:
class EventEmitter { constructor() { this._events = {}; // 保存所有的事件 } on(eventName, callback) { if (!this._events[eventName]) { this._events[eventName] = []; } this._events[eventName].push(callback); } emit(eventName, ...arg) { // 如果on方法监听了多个事件的话,依次执行代码 if (this._events[eventName]) { for (let i = 0; i < this._events[eventName].length; i++) { this._events[eventName][i](...arg); } } } } // 下面是实例化上面的类函数, 然后会依次执行 const eventEmitter = new EventEmitter(); eventEmitter.on(\'kongzhi\', function(age) { console.log(\'我是空智,我今年\' + age + \'岁\'); // 会打印:我是空智,我今年30岁 }); eventEmitter.on(\'kongzhi\', function(age) { console.log(\'我是空智2,我今年\' + age + \'岁\'); // 会打印:我是空智2,我今年30岁 }); eventEmitter.emit(\'kongzhi\', 30);
如上代码就实现了一个类为 EventEmitter 中的on和emit方法了,on是注册事件,emit是触发该事件,然后执行的对应的回调函数,首先通过on去注册事件,然后我们会通过 this._event对象来保存该事件的回调函数,this._event中对象的key就是on注册的事件名,然后on注册的回调函数就是 this._event中使用数组保存起来,该this._event的值就是一个数组函数,然后在emit方法中,使用for循环进行依次执行该回调函数。就会依次触发对应的函数了。
emit(eventName, ...arg) 方法传入的参数,第一个为事件名,其他参数事件对应执行函数中的实参。该方法的作用是:从事件对象中,寻找对应的key为eventName的属性,执行该属性所对应的数组里面的每一个函数。
上面是一个简单的实现,下面我们再来看看 events.js 源码中是如何实现的呢?
events.js 源码分析:
function EventEmitter() { EventEmitter.init.call(this); } module.exports = EventEmitter; EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; EventEmitter.prototype._eventsCount = 0; EventEmitter.prototype._maxListeners = undefined; EventEmitter.init = function() { /* 如果this._event为undefined,或 Object.getPrototypeOf 上面也没有 events的话(es5), 则使用Object.create()创建一个空的对象. 并且设置_eventsCount事件个数为0,也就是初始化。 并且初始化 _maxListeners,默认为10个,未初始化之前是undefined。 */ if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) { this._events = Object.create(null); this._eventsCount = 0; } this._maxListeners = this._maxListeners || undefined; };
如上代码,构造函数 EventEmitter 会调用 EventEmitter.init方法进行初始化,然后使用 this._events = Object.create(null);创建一个对象保存到 this._events中,作用是用于存储和统一管理所有类型的事件,在创建构造函数的时候导出了 EventEmitter,后面所有的方法放在该对象中的原型中。_maxListeners的含义我们上面已经讲解过,是保存最大的监听数。默认为10个。
当然我们可以在源码中使用 getMaxListeners/setMaxListeners方法设置最大的监听数,比如如下的源码:
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { if (typeof n !== \'number\' || n < 0 || NumberIsNaN(n)) { throw new RangeError(\'The value of "n" is out of range. It must be a non-negative number. Received \' + n + \'.\'); } this._maxListeners = n; return this; }; function $getMaxListeners(that) { if (that._maxListeners === undefined) return EventEmitter.defaultMaxListeners; return that._maxListeners; } EventEmitter.prototype.getMaxListeners = function getMaxListeners() { return $getMaxListeners(this); };
在源码中,我们也对 最大的监听数使用了 Object.defineProperty方法进行监听,如下源码所示:
var defaultMaxListeners = 10; Object.defineProperty(EventEmitter, \'defaultMaxListeners\', { enumerable: true, get: function() { return defaultMaxListeners; }, set: function(arg) { if (typeof arg !== \'number\' || arg < 0 || NumberIsNaN(arg)) { throw new RangeError(\'The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received \' + arg + \'.\'); } defaultMaxListeners = arg; } });
2. addEventListener 或 on 添加事件源码如下:
/* addListener函数的别名是on,该函数有两个参数,type是事件名称,listener是监听函数。 之后会调用 _addListener函数进行初始化,从_addListener函数中返回的是this,这样的设计目的是可以进行链式 调用。 */ EventEmitter.prototype.addListener = function addListener(type, listener) { return _addListener(this, type, listener, false); }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; /* 该_addListener函数有四个参数,分别为 target指向了当前this对象。 type为事件名称,listener为监听事件的函数名。 prepend参数如果为true的话,是把监听函数插入到数组的头部,默认为false,插入到数组的尾部。 */ function _addListener(target, type, listener, prepend) { var m; var events; var existing; /* 如果使用on或addEventListener注册事件时,如果listener参数不是一个函数的话,会抛出错误。如下代码。 */ if (typeof listener !== \'function\') { throw new TypeError(\'The "listener" argument must be of type Function. Received type \' + typeof listener); } /* 1. 拿到当前的所有的事件 _events, 即拿到一个事件对象,对象中存放了触发事件组或空对象。 如果this._events是undefined的话,就使用 Object.create(null) 创建一个 空对象给events保存起来。然后设置 this._eventsCount = 0; 用于统计事件的个数,也就是_events对象有多少个属性。 2. 如果有this._event的话,如果使用了on或addEventListener注册了 newListener 事件的话,就直接触发执行它。 */ events = target._events; if (events === undefined) { events = target._events = Object.create(null); target._eventsCount = 0; } else { // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (events.newListener !== undefined) { target.emit(\'newListener\', type, listener.listener ? listener.listener : listener); // Re-assign `events` because a newListener handler could have caused the // this._events to be assigned to a new object /* 重新注册events,因为 newListener钩子可能导致this._event去重新注册个新对象。 */ events = target._events; } // 保存上一次触发的事件 existing = events[type]; } /* exiting变量是保存上一次触发的事件对象,比如:如下测试代码: // 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter); const callback = function() { console.log(11111); }; // 使用on注册监听器 eventEmitter.on(\'kongzhi\', callback); // 注册kongzhi2事件 eventEmitter.on(\'kongzhi\', callback); // 触发事件 kongzhi 是可以触发的 eventEmitter.emit(\'kongzhi\'); 如上基本代码,当我们第一次监听eventEmitter.on(\'kongzhi\')的时候,existing并没有保存该事件对象函数,因此第一次 的时候为undefined,所以会做如下判断 如果为undefined的话,就把 callback函数赋值给 events[type]了。 然后 target._eventsCount 自增1,也就是说 _eventsCount 保存的事件个数加1. 2. 如果 typeof existing 是个函数的话,说明之前注册过一次 \'kongzhi\' 这样的事件,然后继续判断 prepend 是否为 true还是false,为true的话,说明把该函数插入到数组的最前面,否则的话,插入该数组的后面。如果为true,如下代码: events[type] = [listener, existing],否则为false的话,events[type] = [existing, listener], 其中existing 是保存上一次触发的事件对象。然后所有的判断完成后,把最新值重新赋值给 existing 变量。 3. 如果 existing 已经是个数组的话,并且prepend为true的话,直接插入到数组的最前面, 因此执行代码: existing.unshift(listener); 4. 其他的情况就是 prepend 默认为false的情况下,且 existing 为数组的情况下,直接把 监听函数 listener 插入到existing该数组的后面去,如下代码: existing.push(listener); */ if (existing === undefined) { // Optimize the case of one listener. Don\'t need the extra array object. existing = events[type] = listener; ++target._eventsCount; } else { if (typeof existing === \'function\') { // Adding the second element, need to change to array. existing = events[type] = prepend ? [listener, existing] : [existing, listener]; // If we\'ve already got an array, just append. } else if (prepend) { existing.unshift(listener); } else { existing.push(listener); } /* 下面是监听函数最大的数量。如果监听的数量 m > 0 && existing.length(监听数量的长度大于监听的数量的话) > m && !existing.warned 的话。existing.warned 设置为false,目的是打印一次错误即可,因此函数内部代码直接设置 为 existing.warned = true;比如如下代码: // 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter); const callback = function() { console.log(11111); }; // 设置默认监听数量最多为1个,如果超过该数量,控制台会发出警告 eventEmitter.setMaxListeners(1); // 使用on注册监听器 eventEmitter.on(\'kongzhi\', callback); // 注册kongzhi事件 eventEmitter.on(\'kongzhi\', callback); // 触发事件 kongzhi eventEmitter.emit(\'kongzhi\'); 设置监听最大个数为1个,但是实际监听的个数为2,因此在控制台中会输出错误,如下报错信息: (node) warning: possible EventEmitter memory leak detected. 2 listeners added. Use emitter.setMaxListeners() to increase limit. 如上错误就是下面的代码的new Error抛出的。最后通过 执行如下函数抛出,如下代码: function ProcessEmitWarning(warning) { if (console && console.warn) console.warn(warning); } */ // Check for listener leak m = $getMaxListeners(target); if (m > 0 && existing.length > m && !existing.warned) { existing.warned = true; // No error code for this since it is a Warning // eslint-disable-next-line no-restricted-syntax var w = new Error(\'Possible EventEmitter memory leak detected. \' + existing.length + \' \' + String(type) + \' listeners \' + \'added. Use emitter.setMaxListeners() to \' + \'increase limit\'); w.name = \'MaxListenersExceededWarning\'; w.emitter = target; w.type = type; w.count = existing.length; ProcessEmitWarning(w); } } return target; }
3. emit 触发事件的源码如下:
/* 1. 定义一个args数组,来保存所有的emit后面的参数。比如如下代码: // 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter); // 注册 kongzhi 事件 eventEmitter.on(\'kongzhi\', function(age) { console.log(\'dddd\' + age); // 打印 dddd30 }); // 触发kongzhi事件 eventEmitter.emit(\'kongzhi\', 30); 如上代码,emit上的第二个参数传递了30,因此在on监听该对象的时候,该函数会接收age这个参数值为30,因此会打印 dddd30. 如下代码:for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); 就是从第一个参数开始 ,使用 arguments.length 获取函数中所有的参数,依次循环保存到 args数组里面去。注意这边 i 是从1开始的,那么 位置0就是type(类型)。除了触发类型之后所有的变量,也就是所有的参数保存到 args数组内部。 2. 然后判断 type 是否等于 error, 如 var doError = (type === \'error\'); 这句代码会返回一个布尔值。 var events = this._events;保存所有的事件对象,如果 type === ‘error’的话,比如 doError 为true的话, 如果args数组保存的第一个值是Error的实列的话,就抛出该error。否则的话,就抛出如下错误信息: events.js:62 Uncaught Error: Uncaught, unspecified "error" event. (undefined) 如下代码使用 emit来监听 error事件会打印如上的error代码的。如下: // 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter); eventEmitter.emit(\'error\'); 如果触发emit的事件不是error的话,那么获取该事件的对象,如下代码:var handler = events[type]; 如果该事件对象没有函数的话,直接返回,监听对象 on中会报错 listener 必须为一个函数的错误。 如果该handler是一个函数的话,就会执行这个函数。如:ReflectApply(handler, this, args);代码; ReflectApply 封装的源码在events.js中的最上面代码: var R = typeof Reflect === \'object\' ? Reflect : null var ReflectApply = R && typeof R.apply === \'function\' ? R.apply : function ReflectApply(target, receiver, args) { return Function.prototype.apply.call(target, receiver, args); } 想要了解 Reflect.apply的方法的话,可以看我这篇文章 (https://www.cnblogs.com/tugenhua0707/p/10291909.html#_labe2). 因此就能直接执行该回调函数。 2. 如果handler不是一个函数的话,而是一个数组的话,那么就循环该数组,然后依次执行对应的函数。 */ EventEmitter.prototype.emit = function emit(type) { var args = []; for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); var doError = (type === \'error\'); var events = this._events; if (events !== undefined) doError = (doError && events.error === undefined); else if (!doError) return false; // If there is no \'error\' event listener then throw. if (doError) { var er; if (args.length > 0) er = args[0]; if (er instanceof Error) { // Note: The comments on the `throw` lines are intentional, they show // up in Node\'s output if this results in an unhandled exception. throw er; // Unhandled \'error\' event } // At least give some kind of context to the user var err = new Error(\'Unhandled error.\' + (er ? \' (\' + er.message + \')\' : \'\')); err.context = er; throw err; // Unhandled \'error\' event } var handler = events[type]; if (handler === undefined) return false; if (typeof handler === \'function\') { ReflectApply(handler, this, args); } else { var len = handler.length; var listeners = arrayClone(handler, len); for (var i = 0; i < len; ++i) ReflectApply(listeners[i], this, args); } return true; };
4. 删除指定的事件监听器removeListener源码如下:
// 如下基本代码:removeListener的别名是off。 EventEmitter.prototype.removeListener = function removeListener(type, listener) { var list, events, position, i, originalListener; /* 如果removeListener方法中第二个参数不是一个函数的话,抛出错误。必须为一个函数。*/ if (typeof listener !== \'function\') { throw new TypeError(\'The "listener" argument must be of type Function. Received type \' + typeof listener); } /* 获取所有的事件对象,保存到 events变量内。如果事件对象是undefined的话,直接返回。 然后该事件对象 list = events[type]; type 是要被删除的事件名,因此 list 就是保存对应的函数了。 1. 如果list对象函数等于 listener 要删除的对象函数的话,或者 list.listener === listener 的话; 或者匹配的是监听事件key: Function 如下代码: // 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter); const callback = function() { console.log(11111); }; // 使用on注册监听器 eventEmitter.on(\'kongzhi\', callback); // 触发事件 kongzhi eventEmitter.emit(\'kongzhi\'); eventEmitter.removeListener(\'kongzhi\', callback); /* 我们继续触发事件 kongzhi 是不会触发的,因为上面已经使用 removeListener 已经删除了 kongzhi 事件了 */ eventEmitter.emit(\'kongzhi\'); */ events = this._events; if (events === undefined) return this; list = events[type]; if (list === undefined) return this; if (list === listener || list.listener === listener) { /* 如果删除的事件相等的话,--this._eventsCount 自减1. 同时判断 this._eventsCount 如果等于0的话, 则移除所有监听,这里重置监听对象数组。即 this._events = Object.create(null); */ if (--this._eventsCount === 0) this._events = Object.create(null); else { /* 正常执行删除:delete events[type]. 在页面上我们可以来监听 removeListener 事件,比如使用 removeListener 删除事件,我们可以使用on 来监听 removeListener 事件,比如如下代码: // 引入 events 模块 const events = require(\'events\'); console.log(events); // 创建 eventEmitter 对象 const eventEmitter = new events.EventEmitter(); console.log(eventEmitter); const callback = function() { console.log(11111); }; // 使用on注册监听器 eventEmitter.on(\'kongzhi\', callback); // 触发事件 kongzhi eventEmitter.emit(\'kongzhi\'); eventEmitter.on(\'removeListener\', function(type, listener) { console.log(\'这里是来监听删除事件的\'); // 会打印出来 console.log(type); // kongzhi console.log(listener); // callback对应的函数 }); eventEmitter.removeListener(\'kongzhi\', callback); /* 我们继续触发事件 kongzhi 是不会触发的,因为上面已经使用 removeListener 已经删除了 kongzhi 事件了 */ eventEmitter.emit(\'kongzhi\'); */ delete读 Node.js 源码深入理解 cjs 模块系统