ting-emitter源码阅读
12356
源码分析
// 把订阅名称和发布的事件数组对应好关系
on: function (name, callback, ctx) {
// 有就使用e,没有就创建
var e = this.e || (this.e = {});
// 和e一样的处理,创建事件名称数组(可能一个名称触发多个方法)
(e[name] || (e[name] = [])).push({
fn: callback,
ctx: ctx
});
// 可以看到目前全部的订阅关系
return this;
}
// 发布
emit: function (name) {
// data为函数传参
var data = [].slice.call(arguments, 1);
// 兼容写法,拿到对应名称的事件数组
var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
var i = 0;
var len = evtArr.length;
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) {
for (var i = 0, len = evts.length; i < len; i++) {
if (evts[i].fn !== callback && evts[i].fn._ !== callback)
liveEvents.push(evts[i]);
}
}
// 这里原本是使用splice的,换成新数组的原因是因为splice会改变他原本的数组,for循环中会出现问题。
// 取消订阅某个事件之后的事件数组,有长度就替换原来的,没有就直接删除这个属性
// 这里直接删除这个属性可能是为了节省内存,不过delete方法也消耗挺大的
(liveEvents.length)
? e[name] = liveEvents
: delete e[name];
return this;
}
// 订阅一次的事件
once: function (name, callback, ctx) {
var self = this;
// 把事件包装一层,闭包
function listener () {
self.off(name, listener);
callback.apply(ctx, arguments);
};
// 因为原来的事件被包装了一层,所以使用_属性来存一份副本
listener._ = callback
return this.on(name, listener, ctx);
},
应用场景
- 在全局不同地方想要通信时,不能总是在全局定义变量,编程需要遵守一定规范,类似vue中EventBus。
个人理解
- 事件订阅发布也是在面试中常见的一个题目,整体就是使用了一个对象来存储对应的名称与事件关系,触发之后对应事件执行。
- 但是在大一些的项目中,使用过多也会出现不好找的问题,名称必须唯一,在订阅时的事件名称最好也是唯一,不然使用搜索时,会不好找。
- 项目使用都是兼容写法,不需要使用babel来兼容,代码量也很少,应用与一些小项目,小模块,是很不错的选择。