前言
本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
这是源码共读的第8期,链接:【若川视野 x 源码共读】第8期 | mitt、tiny-emitter 发布订阅。
铁子们好,我是跑不快的猪。
这篇文章继续探索发布订阅相关的包模块 mitt ,若有分析错误的地方还希望各位能留言提醒。
本片文章将分为两部分对 mitt 做解读。
- mitt简介
- 转换后的js源码注释
mitt简介
mitt 和 tiny-emitter 的功能相同,都是能更方便快捷的实现发布订阅模式。如果对 tiny-emitter 还不是很了解的朋友,可以参照上一篇文章:《发布订阅模式(一):tiny-emitter》。虽然两个包模块功能相同,但是实现的方式以及使用上还是有不小的差别。
mitt的github地址:https://github.com/developit/mitt
mitt 暴露的api如下:
- on:订阅一个事件。
- off:根据给定的名称,取消订阅事件。
- emit:触发订阅事件的方法。
这里只给出了暴露的api方法名称,方便后续解析源码。具体参数可以直接参照github源码地址,作者给出的示例中解释的很详细。
转换后的js源码
这里先放出来转换后的js源码,对整体功能做一个了解,所以即便没有typescript 功底的朋友也能够轻易的阅读。如果大家想自行调试,需要将代码通过压缩包的方式下载下来。因为通过包模块管理器下载下来的包模块是压缩之后的代码,不适合阅读。
exports.__esModule = true;
function mitt(all) {
// 声明一个Map类型,作为整体事件处理中心
all = all || new Map();
return {
all,
on (type, handler) {
// 先获取是否有对应事件的处理函数数组
let handlers = all.get(type);
if (handlers) {
// 将新注册的函数推送到对应时间的函数数组中
handlers.push(handler);
}
else {
// 创建一个对应 type 的函数数组
all.set(type, [handler]);
}
},
off (type, handler) {
// 从map中取出所有的对应的函数数组
let handlers = all.get(type);
if (handlers) {
// 如果存在hanlder,进行筛选后,从数组中删除。
// 如果不存在直接清空当前事件所有的处理函数
if (handler) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
else {
all.set(type, []);
}
}
},
emit (type, evt) {
let handlers = all.get(type);
// 按照取出的函数数组遍历循环执行
if (handlers) {
handlers
.slice()
.map((handler) => {
handler(evt);
});
}
// 如果注册了 "*" 的事件,执行它
handlers = all.get('*');
if (handlers) {
handlers
.slice()
.map((handler) => {
handler(type, evt);
});
}
}
};
}
exports.default = mitt;
转换后的源码看起来一目了然,在阅读到 off 方法的时候,其中的位运算符 >>> 还是让我觉得眼前一亮。我们单摘下面代码:
...
if (handler) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
...
根据上下文知道,这里是在我们调用 off 方法的时候将传入的回调函数进行一个对比,如果在实践中找到要取消的事件对应的回调函数,那么就将这个函数从事件中心中去掉。去掉的方式是通过 splice 完成的,而在查找对应回调的时候用了一个我个人平常基本不用的位运算符 >>> 。
>>> 是无符号右移的意思。
-9 (base 10): 11111111111111111111111111110111 (base 2)
--------------------------------
-9 >>> 2 (base 10): 00111111111111111111111111111101 (base 2) = 1073741821 (base 10)
如上代码,在-9进行右移2位的时候,左面会空出两位,那这两位就无脑补0,就可以。
还有一个带符号右移的位运算符 >> ,它在右移之后空出的位置就不能无脑补充0,而是要按照符号位进行补充符号位。
在源码中,如果搜索不到相应的回调函数。indexOf(handler) 会返回 -1。这个时候经过右移 0 位,它的值就变成了 4294967295 ,如此一来我们进行数组替换的时候,就不用担心会替换掉有用的元素。
这里还有一个知识点,就是一个数组的长度是有最大值的。所以不用担心你的数组有超级长包括了 4294967295 这么多。因为 new Array(length) 中的 length 的取值范围是0 到 2^32 - 1 中的整数。