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 events[type];
if (events.removeListener)
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') {
/*
这里是其他情况,如果list监听的不是一个函数的话,而是一个数组的话,比如on监听的多个相同的事件名的时候,则遍历该数组,同样判断,如果该数组的任何一项等于被删除掉的 listener函数的话,
或者数组中的任何一项的key===listener 等于被删除掉的listener函数的话,使用 originalListener = list[i].listener; 保存起来,同样使用 position = i; 保存该对应的位置。跳出for循环。
2. if (position < 0) 如果该 position 小于0的话,直接返回。说明没有要删除的事件。
3. 如果position === 0的话,则删除数组中的第一个元素,使用 list.shift(),移除数组中的第一个元素。
4. 否则的话调用 spliceOne 方法,删除数组list中的对应位置的元素,该spliceOne方法代码如下:
function spliceOne(list, index) {
for (; index + 1 < list.length; index++)
list[index] = list[index + 1];
list.pop();
}
*/
position = -1; for (i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || list[i].listener === listener) {
originalListener = list[i].listener;
position = i;
break;
}
} if (position < 0)
return this; if (position === 0)
list.shift();
else {
spliceOne(list, position);
}
// 如果list.length === 1 的话,events[type] = list[0].
if (list.length === 1)
events[type] = list[0]; if (events.removeListener !== undefined)
this.emit('removeListener', type, originalListener || listener);
} return this;
}; EventEmitter.prototype.off = EventEmitter.prototype.removeListener;

5. 删除所有的监听器 removeAllListeners 源码如下:

/*
removeAllListeners该函数接收一个参数type,事件名称。
*/
EventEmitter.prototype.removeAllListeners =
function removeAllListeners(type) {
var listeners, events, i; events = this._events;
// 如果 保存的事件对象 events 为undefined的话,直接返回
if (events === undefined)
return this; // not listening for removeListener, no need to emit
/*
1. 如果 this._events保存的事件对象的removeListener为undefined的话。说明没有使用 removeListener来进行
删除事件。
2. 如果 arguments.length === 0 则是删除所有的事件。因为如果removeAllListeners没有指定事件名称的话,
则是删除所有的事件的。因此直接设置 this._events = Object.create(null); 为空对象,并且 this._eventsCount = 0; 监听的事件数量为0.
3. 如果 events[type] !== undefined 不等于undefined,说明type有的话,则是删除该事件名的所有事件。
--this._eventsCount 自减1. 并且如果 this._eventsCount === 0 等于0 的话,则:
this._events = Object.create(null); 设置空对象。
4. 如果 this._eventsCount 不等于0 的话,则删除该指定的事件的所有事件名称。最后返回该this对象。
*/
if (events.removeListener === undefined) {
if (arguments.length === 0) {
this._events = Object.create(null);
this._eventsCount = 0;
} else if (events[type] !== undefined) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
else
delete events[type];
}
return this;
} // emit removeListener for all listeners on all events
/*
1. 如果 arguments.length === 0 等于0的话,则是删除所有的事件。先使用 var keys = Object.keys(events);
获取所有的keys。然后使用for循环依次遍历,得到某一个事件key。然后依次使用 this.removeAllListeners(key);
方法递归调用删除对应的key。
2. 最后执行 this._events = Object.create(null); 设置为空对象。置空。
3. this._eventsCount = 0; 事件的个数设置为0. 最后返回该this对象。
*/
if (arguments.length === 0) {
var keys = Object.keys(events);
var key;
for (i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = Object.create(null);
this._eventsCount = 0;
return this;
}
/*
1. 如果该 events[type] 是一个函数的话,则调用 this.removeListener(type, listeners); 删除该事件对应
的函数。
2. 其他的情况就是 listeners 不等于 undefined的话。说明是一个数组的话,那么依次循环该数组,然后依次使用
removeListener方法删除该事件对应的函数。最后返回this对象。使可以链式调用。
*/
listeners = events[type]; if (typeof listeners === 'function') {
this.removeListener(type, listeners);
} else if (listeners !== undefined) {
// LIFO order
for (i = listeners.length - 1; i >= 0; i--) {
this.removeListener(type, listeners[i]);
}
}
return this;
};

6. 返回指定事件的监听器数组--listeners源码如下:

function _listeners(target, type, unwrap) {
var events = target._events; if (events === undefined)
return []; var evlistener = events[type];
if (evlistener === undefined)
return [];
/*
1. 如果events[type]是一个函数的话,就返回 [evlistener.listener || evlistener], 则返回该数组函数。
2. 否则的话,则返回 该数组函数。
*/
if (typeof evlistener === 'function')
return unwrap ? [evlistener.listener || evlistener] : [evlistener]; return unwrap ?
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}
function unwrapListeners(arr) {
var ret = new Array(arr.length);
for (var i = 0; i < ret.length; ++i) {
ret[i] = arr[i].listener || arr[i];
}
return ret;
}
function arrayClone(arr, n) {
var copy = new Array(n);
for (var i = 0; i < n; ++i)
copy[i] = arr[i];
return copy;
}
EventEmitter.prototype.listeners = function listeners(type) {
return _listeners(this, type, true);
};

如下代码演示:

// 引入 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
});

7. 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;
}

还有其他几个简单的方法 once, eventNames 可以自己看下源码了。

深入理解 Node.js 中 EventEmitter源码分析(3.0.0版本)的更多相关文章

  1. 从发布订阅模式入手读懂Node.js的EventEmitter源码

    前面一篇文章setTimeout和setImmediate到底谁先执行,本文让你彻底理解Event Loop详细讲解了浏览器和Node.js的异步API及其底层原理Event Loop.本文会讲一下不 ...

  2. 深入理解Node.js中的垃圾回收和内存泄漏的捕获

    深入理解Node.js中的垃圾回收和内存泄漏的捕获 文章来自:http://wwsun.github.io/posts/understanding-nodejs-gc.html Jan 5, 2016 ...

  3. php中foreach源码分析(编译原理)

    php中foreach源码分析(编译原理) 一.总结 编译原理(lex and yacc)的知识 二.php中foreach源码分析 foreach是PHP中很常用的一个用作数组循环的控制语句.因为它 ...

  4. Vue系列---理解Vue.nextTick使用及源码分析(五)

    _ 阅读目录 一. 什么是Vue.nextTick()? 二. Vue.nextTick()方法的应用场景有哪些? 2.1 更改数据后,进行节点DOM操作. 2.2 在created生命周期中进行DO ...

  5. 手把手教你实现栈以及C#中Stack源码分析

    定义 栈又名堆栈,是一种操作受限的线性表,仅能在表尾进行插入和删除操作. 它的特点是先进后出,就好比我们往桶里面放盘子,放的时候都是从下往上一个一个放(入栈),取的时候只能从上往下一个一个取(出栈), ...

  6. 【原】Spark中Client源码分析(二)

    继续前一篇的内容.前一篇内容为: Spark中Client源码分析(一)http://www.cnblogs.com/yourarebest/p/5313006.html DriverClient中的 ...

  7. 【原】Spark中Master源码分析(二)

    继续上一篇的内容.上一篇的内容为: Spark中Master源码分析(一) http://www.cnblogs.com/yourarebest/p/5312965.html 4.receive方法, ...

  8. 【原】 Spark中Worker源码分析(二)

    继续前一篇的内容.前一篇内容为: Spark中Worker源码分析(一)http://www.cnblogs.com/yourarebest/p/5300202.html 4.receive方法, r ...

  9. 《深入理解Spark-核心思想与源码分析》(一)总体规划和第一章环境准备

    <深入理解Spark 核心思想与源码分析> 耿嘉安著 本书共计486页,计划每天读书20页,计划25天完成. 2018-12-20   1-20页 凡事豫则立,不豫则废:言前定,则不跲:事 ...

随机推荐

  1. [转]JS学习总结-技巧、方法、细节

    变量转换 var myVar = "3.14159", str = ""+ myVar,// string类型 int = ~~myVar, // number ...

  2. POJ 2942Knights of the Round Table(tarjan求点双+二分图染色)

    Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 13954   Accepted: 4673 Description Bein ...

  3. Jenkins 利用Build With Parameters Plugin实现Jenkins参数化构建

    利用Build With Parameters Plugin实现Jenkins参数化构建   by:授客 QQ:1033553122 测试环境 Build With Parameters Plugin ...

  4. 实现加载Tomcat服务器中的图片,并且有进度条提示进度

    首先布局页面, <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andr ...

  5. mysql左连接

    举个例子说明: select d.id, d.uid,d.username,d.dateline, d.message,d.replynum, c.picid, c.filename from doi ...

  6. Stable Fur Generation on Mesh

    After tested the Maya 2015 XGen Grooming, we dropped it, that's really slow and unstable, totally no ...

  7. vue缓存页面【二】

    keep-alive是vue内置的一个组件,可以使被它包含的组件处于保留状态,或避免被重新渲染. 用法:运行结果描述:input输入框内,路由切换输入框内部的内容不会发生改变.在keep-alive标 ...

  8. vi中的全局替换

    一.基本语法 替换命令语法: :[addr]s/源字符串/目标字符串/[option] 全局替换: :%s/源字符串/目标字符串/g 参数说明: [addr]--表示检索范围,省略时表示当前行. &q ...

  9. [20190214]11g Query Result Cache RC Latches.txt

    [20190214]11g Query Result Cache RC Latches.txt --//昨天我重复链接http://www.pythian.com/blog/oracle-11g-qu ...

  10. C#-异常处理(十四)

    概念 异常处理是指程序在运行过程中,发生错误会导致程序退出,这种错误,就叫做异常 但并不是所有的错误都是异常 而处理这种错误,称为异常处理 异常处理实际是不断去发掘异常.修改异常,使程序更稳定 异常处 ...