关于 event 菜狗子在这作点简单的整理下而已。 有错误欢迎指出斧正javascript
在node中咱们要实现一套事件机制真的是无比简单的。直接继承下原生的EventEmitter
完成~。菜狗子比较闲,就和你们一块儿读读这部分的源码。java
源码地址node
/** 发布一个事件,这部分比较简单 */
EventEmitter.prototype.emit = function emit(type, ...args) {
const events = this._events;
/** * 这里是一大段错误处理,附加错误信息的代码 * 有几个辅助的函数,想了解建议看源码。 * tips:同时会对`error`这个时间进行处理。(若是没有监听error的话) * 这里其实安利一个调试利器 * 原生函数:Error.captureStackTrace 挂载调用栈。 * 之后获取调用栈就省的递归callee或者扔一个没用的错误了 * @link */
/** on 或者 addListener 的时候挂载的*/
const handler = events[type];
if (handler === undefined)
// 这里能够看到哈,返回值决定了是否有被监听捕获,某些状况也许就用上饿了
return false;
if (typeof handler === 'function') {
/** 这里效果和Function.prototype.apply一致 */
Reflect.apply(handler, this, args);
} else {
/** 这个len能让程序稍稍快一点,比起每次循环都去求算。常常被忽略*/
const len = handler.length;
const listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
Reflect.apply(listeners[i], this, args);
}
return true;
};
复制代码
监听一个事件,这部分其实也比较简单git
/** * 菜狗子手动注释TAT * @param { EventEmitter } target event 实例 * @param { string } type 事件名称 * @pram { function } listener 回调事件,this指向当前event对象 * @pram { boolean } prepend 是否往前插入 * 没想到吧人家支持插入在前面来解决事件调用顺序的问题 * 可是on是日后插,插来插去的怎么感受好污 * 往前插入请用prependListener */
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
// 检查你给的监听器是不是函数
checkListener(listener);
events = target._events;
if (events === undefined) {
// Object.create 这个小技巧就不用介绍了吧,省内存。
events = target._events = Object.create(null);
// event 监听器是有限制的,如下是为啥要有限制的解释,就是方便排查内存问题
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
target._eventsCount = 0;
} else {
// 这块 newListener 是不少其余模块有用到,好比https啥的。
if (events.newListener !== undefined) {
target.emit('newListener', type,
listener.listener ? listener.listener : listener);
events = target._events;
}
existing = events[type];
}
// 这里我有些不理解为啥要用两种数据结构存监听器。。
// 一个的时候是函数,多个的是才存成数组。感受就是给本身挖坑啊!!
if (existing === undefined) {
// Optimize the case of one listener. Don't need the extra array object.
events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === 'function') {
existing = events[type] =
prepend ? [listener, existing] : [existing, listener];
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
// 一部分检查监听器数量的代码,超过就给你报错
}
// 优雅的链式调用。。。
return target;
}
复制代码
once的实现,也十分简单,不赘述了,就是简单的把上面_addListener包裹下 这种高阶函数的写法却是比某些人重写一个要好得多github
function onceWrapper(...args) {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
return Reflect.apply(this.listener, this.target, args);
}
}
function _onceWrap(target, type, listener) {
const state = { fired: false, wrapFn: undefined, target, type, listener };
const wrapped = onceWrapper.bind(state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
复制代码
event还提供了一些获取监听函数的,事件名称,设置最大监听数的函数。就不赘述了。算法
最后竟然还提供了一个once函数用来触发某个事件一次 ,能够说是很良心了数组
在浏览器端使用事件机制咱们每每会找个库或者本身写一个事件机制。其实浏览器原生就有。(兼容性本身测试哈,只作科普向)浏览器
文档看这个数据结构
const eventBus = document.createElement('div')
const customEvent = new CustomEvent('demo',{
test:'test'
})
eventBus.addEventListener('demo',function(e){
console.log(e)
})
eventBus.dispatchEvent(customEvent);
复制代码
为啥没有图片?由于用有道云写的。上传要高贵的会员,做图还麻烦。分享只是举手之劳。app
超级排序算法压轴
function sortArr(arr){
arr.forEach(el=>setTimeout(_=>console.log(el),el))
}
复制代码