本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
tiny-emitter源码
function E () {
// Keep this empty so it's easier to inherit from
// (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
}
E.prototype = {
on: function (name, callback, ctx) {
var e = this.e || (this.e = {});
// e.name = e.name ? e.name : e.name = [];
// e.name.push({fn: callback, ctx})
(e[name] || (e[name] = [])).push({
fn: callback,
ctx: ctx
});
return this; // 为了可以链式调用
},
/** e.once('data', getData) // 只emit一次
e.emit('data') // 第一次调用的时候已经删除了 绑定在对象上的事件
e.emit('data')
e.emit('data')
*/
once: function (name, callback, ctx) {
var self = this;
function listener() {
self.off(name, listener); // 先删除
callback.apply(ctx, arguments); // 再调用
};
listener._ = callback // 在listener函数上面绑一个_属性
// 然后注册当前的事件,如果当前once绑定的事件 emit了,就会先删除,然后再调用,所以在第二次调用的时候,就不会在触发第二次了
return this.on(name, listener, ctx);
},
emit: function (name) {
var data = [].slice.call(arguments, 1); // 获取获取传入的其他参数
// 做一下浅拷贝,防止在emit的时候。引用做了其他改变。
// 如果once的时候已经删除了,这个数组就取不到值,自然就不能到循环,也不会调用函数了
var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
var i = 0;
var len = evtArr.length;
// 依次循环调用传入的函数,这个名字绑定了多少个,就会emit触发多少个
for (i; i < len; i++) {
evtArr[i].fn.apply(evtArr[i].ctx, data);
}
return this;
},
off: function (name, callback) { // 删除当前绑定的函数
var e = this.e || (this.e = {});
var evts = e[name]; // 取出当前注册的队列
var liveEvents = [];
if (evts && callback) { // 如果没有队列,或者没有传入callback,就不做任何处理,(理论上应该报个错吧)
for (var i = 0, len = evts.length; i < len; i++) { // 如果有就进行循环
// 只要有一个没有命中,又不是 on 绑定的,也不是 once 绑定的,就说明啥也没有绑。
if (evts[i].fn !== callback && evts[i].fn._ !== callback)
liveEvents.push(evts[i]);
}
}
// Remove event from queue to prevent memory leak // 预防内存溢出,加了一步删除对象属性的工作
// Suggested by https://github.com/lazd
// 这个里面说:如果你总是在on的时候用push 在off 的时候用splice 可能会有意想不到的问题。然后作者说你说的对,改成了现在这种用arr收集的。这样就不会同时操作同一个引用。造成问题
// Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
(liveEvents.length)
? e[name] = liveEvents // 如果啥也没绑。就把收集的函数,在还给e[name]对象
: delete e[name]; // 只要有一个命中就删除对应的对象,会整个数组都删除哦
return this;
}
};
module.exports = E;
module.exports.TinyEmitter = E;
1. Keep this empty so it's easier to inherit
注释里写了这样一句话。就是要把函数边空,让类更好继承,就不用非要写 call来处理this.e
因为使用当前发布订阅函数的时候,会继承使用, 可以使用es6继承
class Child extends E {
constructor(){
super()
}
}
所以这里作者,用一个var e=this.e || (this.e = {})默认值,代替了声明在类的constructor中的this.e
2. 浅拷贝slice()
((this.e || (this.e = {}))[name] || []).slice(); 在emit的时候 作者做了浅拷贝,原因是:当前数组是一个引用。如果用户在emit的时候有对当前引用做其他操作,可能会有问题。
\
3. 数组引用删除
用splice删除会改变原数组引用,要谨慎使用。
mitt源码
export type EventType = string | symbol;
// An event handler can take an optional event argument
// and should not return a value
export type Handler<T = unknown> = (event: T) => void;
export type WildcardHandler<T = Record<string, unknown>> = (
type: keyof T,
event: T[keyof T]
) => void;
// An array of all currently registered event handlers for a type
export type EventHandlerList<T = unknown> = Array<Handler<T>>;
export type WildCardEventHandlerList<T = Record<string, unknown>> = Array<WildcardHandler<T>>;
// A map of event types and their corresponding event handlers.
export type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<
keyof Events | '*',
EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events>
>;
export interface Emitter<Events extends Record<EventType, unknown>> {
all: EventHandlerMap<Events>;
on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void;
on(type: '*', handler: WildcardHandler<Events>): void;
off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>): void;
off(type: '*', handler: WildcardHandler<Events>): void;
emit<Key extends keyof Events>(type: Key, event: Events[Key]): void;
emit<Key extends keyof Events>(type: undefined extends Events[Key] ? Key : never): void;
}
/**
* Mitt: Tiny (~200b) functional event emitter / pubsub.
* @name mitt
* @returns {Mitt}
*/
export default function mitt<Events extends Record<EventType, unknown>>(
all?: EventHandlerMap<Events>
): Emitter<Events> {
type GenericEventHandler =
| Handler<Events[keyof Events]>
| WildcardHandler<Events>;
all = all || new Map();
return {
/**
* A Map of event names to registered handler functions.
*/
all,
/**
* Register an event handler for the given type.
* @param {string|symbol} type Type of event to listen for, or `'*'` for all events
* @param {Function} handler Function to call in response to given event
* @memberOf mitt
*/
on<Key extends keyof Events>(type: Key, handler: GenericEventHandler) {
/**
* 下面的逻辑如果写成对象就是这样:对象的key只能是字符串,map的key可以用symbol function number 等等
* all[type] ? all[type].push(handles) : all[type] = [handles]
*/
const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
if (handlers) {
handlers.push(handler);
}
else {
all!.set(type, [handler] as EventHandlerList<Events[keyof Events]>);
}
},
/**
* Remove an event handler for the given type.
* If `handler` is omitted, all handlers of the given type are removed.
* @param {string|symbol} type Type of event to unregister `handler` from (`'*'` to remove a wildcard handler)
* @param {Function} [handler] Handler function to remove
* @memberOf mitt
*/
off<Key extends keyof Events>(type: Key, handler?: GenericEventHandler) {
const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
if (handlers) {
if (handler) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
else {
// 只传了type 会删除所有当前类型的函数
all!.set(type, []);
}
}
},
/**
* Invoke all handlers for the given type.
* If present, `'*'` handlers are invoked after type-matched handlers.
*
* Note: Manually firing '*' handlers is not supported.
*
* @param {string|symbol} type The event type to invoke
* @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
* @memberOf mitt
*/
emit<Key extends keyof Events>(type: Key, evt?: Events[Key]) {
let handlers = all!.get(type);
if (handlers) { // 如果获取到了处理数组
// 先拷贝,在处理,我理解用map,foreach应该都可以,可能map更符合函数式编程?
(handlers as EventHandlerList<Events[keyof Events]>)
.slice()
.map((handler) => {
handler(evt!); // 函数的参数。没有做多参数处理,作者建议传个对象
});
}
handlers = all!.get('*'); // 如果当前的type是全部, 监听所有的事件。不知道这有什么应用场景
if (handlers) {
(handlers as WildCardEventHandlerList<Events>)
.slice()
.map((handler) => {
handler(type, evt!);
});
}
}
};
}
1. Map 和 Object
简化一下on逻辑
all = new Map()
on(type, handler){
// 如果有,就push到数组中,没有就初始化一个数组
all.has(type) ? all.get(type).push(handler) : all.set(type, [handler])
}
如果on的代码用下面的对象写的话。
all = {}
on(type, handler){
all[type] ? all[type].push(handler) : all[type] = [handler]
}
- 两个其实逻辑一致,不一样的就是map的key是可以用任意值(可以是function,symbol,object,number等)的,而obj的key只能是string
- 在内存占用和垃圾回收上的区别 (我也不太会。后面补 TODO)
- map中的clear() 等方法,都可以直接使用。all.clear()
2. 位运算
位运算:www.w3school.com.cn/js/js_bitwi…
无符号右移 缺位0来补
负数:-1 >>> 0 会得到2^32-1这个很大的数。就相当于没删除
正数:1 >>> 0 不会改变当前正数,所以就能找到当前function删除
有符号右移>>,缺位符号位来补,如果负数就补1 正数就补0
负数 最大 2 ^ 31
正数 最大 2 ^ 31 - 1 因为0 算在正数这边,占掉了一位
~ 取反
-n = ~n + 1
n = ~ -n + 1
| 只要有1 就为1
& 两个都为1 才为1
^ 相同为1 不同为0
Node中的events模块
node中的events模块,是node的核心模块
const Events = require('events');
基本api 有 on off emit once 还提供了一个newListener事件,监听绑定的事件。如果绑定事件会自动触发该函数,并返回绑定直接的name
function Events() {
this._events = Object.create(null);
}
Events.prototype.on = function (name, fn) {
if (!this._events) this._events = {};
// 如果当前注册的应用不是newListener, 就会触发newListener,返回当前注册的事件名称
/*
events.on('newListener', (name) => { console.log(name) }) // 输出 data data1
events.on('data', () => {})
events.on('data1', () => {})
*/
if (name !== 'newListener') {
this.emit('newListener', name)
}
(this._events[name] || (this._events[name] = [])).push(fn)
}
Events.prototype.once = function (name, fn) {
if (!this._events) this._events = {};
const once = (...args) => { // 先触发,再删除
fn(...args);
this.off(name, once);
}
// 在once上绑定当前fn,不然删除不掉。这里因为包了一层函数,所以要让包的那层函数和之前的函数有点关系
once._ = fn
this.on(name, once);
}
Events.prototype.emit = function (name, ...args) {
if (!this._events) this._events = {};
const handlers = this._events[name];
if (handlers) {
// 触发就是从数组中依次取出函数调用,与promise.then逻辑一致
handlers.slice().forEach(fn => fn(...args))
}
}
Events.prototype.off = function (name, fn) {
if (!this._events) this._events = {};
const handlers = this._events[name];
if (handlers) {
// 过滤掉,相等的。剩下的都是不相等的。
this._events[name] = handlers.slice().filter(item => item !== fn && item._ !== fn)
}
}
module.exports = Events;