发布订阅模式(二):mitt

1,203 阅读4分钟

前言

本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。

这是源码共读的第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 中的整数。