源码分析
- 环境:Node v14.19.1
调试代码
const event = require('events');
const eventEmitter = new event();
eventEmitter.on('event', () => {
console.log('ai yo.');
});
eventEmitter.on('event', () => {
console.log('yo.');
});
eventEmitter.emit('event');
咱们就按照这个进行分析源码如何实现的
events.js#EventEmitter.init()
该方法实例化事件时执行,说白了new的时候会调用它进行初始化
EventEmitter.init = function(opts) {
// 判断_events是否不存在,不存在即创建空对象
if (this._events === undefined ||
this._events === ObjectGetPrototypeOf(this)._events) {
// 这里有点awesome,这里创建的不是普通的{}字面量对象,普通的字面量的原型链是指向Object.prototype的,这里的对象没有任何原型链,真就存粹的{}对象,这里的做法是减少原型链查找提升效率
// 不信看下面的图纯对象
this._events = ObjectCreate(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || undefined;
// 这里是初始化穿参相关
if (opts?.captureRejections) {
if (typeof opts.captureRejections !== 'boolean') {
throw new ERR_INVALID_ARG_TYPE('options.captureRejections',
'boolean', opts.captureRejections);
}
this[kCapture] = Boolean(opts.captureRejections);
} else {
// Assigning the kCapture property directly saves an expensive
// prototype lookup in a very sensitive hot path.``
this[kCapture] = EventEmitter.prototype[kCapture];
}
};
-
纯对象
这里的方法跟Object.create(null)一致,创建了一个不带原型链的对象
event.js#_addListener()
订阅事件,多个、单个不同的处理
// target: 咱刚才创建的EventEmitter类
// type: 订阅的事件名,咱们代码里面名为'event'的字符串
// listener: 订阅回调函数
// prepend: 默认都是false,用于代码中listener排列的顺序
function _addListener(target, type, listener, prepend) {
let m;
let events; // 事件缓存
let existing;
// 断言:检查你穿进来的回调函数是不是函数,不是函数抛异常
checkListener(listener);
events = target._events;
// 不存咱直接创建,兜底操作,这一步肯定跳过,咱之前实例化就创建过
if (events === undefined) {
events = target._events = ObjectCreate(null);
target._eventsCount = 0;
} else {
// 如果有'newListener'事件即执行它,这里`跳过`,咱根本没有
if (events.newListener !== undefined) {
target.emit('newListener', type,
listener.listener ? listener.listener : listener);
// 可能'newListener'会导致_event变化,确保正确性
events = target._events;
}
// 赋值用于:检查指定的key: 'event'事件是否存在
existing = events[type];
}
// 这里是第一次!!!
// 很好,这里来校验key: 'event'事件是否存在
if (existing === undefined) {
// 这里不用数组是考虑到可能只有一个监听器的情况节省内存,毕竟咱v8 js对象可用内存本来就少,在底层做好优化,才能让应用层浪费~
// 这里放入容器缓存呗,没啥的
events[type] = listener;
++target._eventsCount;
}
// 这里是第二次!!!
else {
// 这里肯定要执行,因为第一次咱们就是搞的如下结构
/*
{
event: () => { console.log('ai yo.'); }
}
*/
if (typeof existing === 'function') {
// 将events容器结构改为数组并添加咱们第二次订阅的回调函数
// 默认prepend为false,所以数组添加是顺序的
existing = events[type] =
prepend ? [listener, existing] : [existing, listener];
}
// 如果是第三次插入,即结构如下
/*
{
event: [
() => {},
() => {}
]
}
*/
// 默认prepend为false,这里不会执行,如果为true就是从头部插入
else if (prepend) {
existing.unshift(listener);
}
// 默认从尾部插入
else {
existing.push(listener);
}
// 检查订阅是否超过最大数量
m = _getMaxListeners(target);
// 超出最大数量给一个警告,最大数量为10
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
const w = new Error('Possible EventEmitter memory leak detected. ' +
`${existing.length} ${String(type)} listeners ` +
`added to ${inspect(target, { depth: -1 })}. Use ` +
'emitter.setMaxListeners() to increase limit');
w.name = 'MaxListenersExceededWarning';
w.emitter = target;
w.type = type;
w.count = existing.length;
process.emitWarning(w);
}
}
// 返回EventEmitter自己,估计为了链式编程方便
return target;
}
event.js#EventEmitter.prototype.emit()
发射指定事件
EventEmitter.prototype.emit = function emit(type, ...args) {
let doError = (type === 'error'); // 判断发布的是不是'error'事件
// 获取容器
const events = this._events;
// 容器存在内容
if (events !== undefined) {
// 执行'error'事件 `咱们不执行`
if (doError && events[kErrorMonitor] !== undefined)
this.emit(kErrorMonitor, ...args);
// 执行完error打一个tag
doError = (doError && events.error === undefined);
}
// 不存内容且发布的'error'事件,直接返回false `跳过`
else if (!doError)
return false;
// 根据上面执行'error'事件后打的tag,如果有错误抛出则执行该代码 `咱们不执行`
if (doError) {
let er;
if (args.length > 0)
er = args[0];
if (er instanceof Error) {
try {
const capture = {};
ErrorCaptureStackTrace(capture, EventEmitter.prototype.emit);
ObjectDefineProperty(er, kEnhanceStackBeforeInspector, {
value: enhanceStackTrace.bind(this, er, capture),
configurable: true
});
} catch {}
// 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
}
let stringifiedEr;
const { inspect } = require('internal/util/inspect');
try {
stringifiedEr = inspect(er);
} catch {
stringifiedEr = er;
}
// At least give some kind of context to the user
const err = new ERR_UNHANDLED_ERROR(stringifiedEr);
err.context = er;
throw err; // Unhandled 'error' event
}
// 从这里才开始真正执行emit()逻辑
// 获取回调函数
const handler = events[type];
// 没有执行的逻辑直接返回
if (handler === undefined)
return false;
// 如果值为单个函数
/*
{
event: () => {}
}
*/
if (typeof handler === 'function') {
// 执行回调
const result = handler.apply(this, args);
// 如果有返回值执行该方法,`跳过`
if (result !== undefined && result !== null) {
addCatch(this, result, type, args);
}
} else {
// 当是函数数组时,迭代执行
const len = handler.length;
const listeners = arrayClone(handler);
for (let i = 0; i < len; ++i) {
const result = listeners[i].apply(this, args);
if (result !== undefined && result !== null) {
addCatch(this, result, type, args);
}
}
}
return true;
};
图解
简单实现EventEmitter
EventEmitter使用的经典的发布者订阅者模型
传送门:简单实现EventEmitter