JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)

上篇博客我们已经知道了事件队列的本质,那么这里我们就来自己搞一个。
首先,它是一个 class:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

然后 constructor 里面,别的不太需要,我就需要一个 pieps:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

为什么是 pieps,带 s 的呢?因为我同一个管道,我是可以同时处理很多种事件的。
我希望的是,你可以给我好多种事件,我都可以认,这个是没问题的,所以我们这是个 pipes,带 s 的。

然后在我这,我会有两种人。
一种是,申明我需要监听某种事件。
而另一种人是说,我这发生了某个事件,它们都跟我打交道。
所以这时候,我需要 2 个方法。

一个是注册这个监听的,我就叫它 addListener:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

当然顺便一说,如果用过 vue,$on 这个名字你可能会更加熟系一些:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

这个先不管,名字叫啥都行,我们先用 addListener 这个。
然后我们 addListener 的时候,需要申明几件事,首先,你想监听对吧?
没问题,但你得告诉我,你要监听什么事件,所以我们需要一个 type:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

以及接下来,我们除了 type 以外,还需要一个 function:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

就是将来这个事件来了,我怎么告诉你,你得给我说呀。

那么接下来就非常简单了,因为我的 pipes 默认都是空的,就是一个 json。
所以我们可以这么来:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

说白了就是,如果它本来这个 type 就存在,那我就直接用以前的。
如果没有,那我就初始化一个空数组。
为什么是数组?很简单,因为同一个事件可能有好多个人都在等,所以他们在一个数组里面。

然后接下来,我就在给它再 push 一个新人,又多一个要监听的:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

就这么简单。
【JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)】
接下来我这还有一个,我叫它 dispatch:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

这个 dispatch,就是我要触发一个事件了。
那么我得告诉它我要触发什么事件吧?所以在这还需要个 type:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

以及,我们还有一些参数:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

之所以这么写,原因很简单,你可以随便传参数,传 100 个都行。

然后接下来,我们就来看一看:
如果 this.pipes 里面的 type 是有的,换句话说,就是以前有人去注册过这个事件。
如果压根没人注册过,那我就什么都不管,就仍了:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

那大家可能会奇怪,万一这个事件没人监听,它不应该存起来吗,等以后有人监听了在给?
其实一般是不会的,像是DOM事件,其实也是这样的。
比如我们 js 进阶五十用的例子:
假设按钮 2 这只是有 dispatch,但是就没有人监听这个事件。
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

那么这时候你可以看到,我点了就是白点。
也不会报错,也什么都不会发生,就是没人监听。

然后直到某一天,有人监听了。
比方说,我们加个 setTimeout:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

那么你可以看到,在 console.log('定时器触发了') 出来之前,怎么点都是没反应的。
因为现在就没人盯着这个事件。

直到有一天,有人开始监听这个事件了,就是 console.log('定时器触发了') 出来之后。
然后这时候我再点,才有反应:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

这很正常。
就是说,之前没人监听的那个时候,那个事件就是没的,消失的。
这很正常,所有的事件监听器都是这么工作的。

然后我们继续。
那如果有人监听这个事,别忘了我是个数组,不一定只是一个人,也可能是一堆人,那么这时候我就来 forEach 一下。
别忘了,我们这个数组里面放的都是函数,那么我要做的就非常的简单,我就执行这个函数,并且把 args 给它带进去:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

那么到这为止,我们这个事件队列就已经定义完了。
代码绝对不复杂,但这个东西它胜在本身很巧妙。

接下来我们来试试。
比如现在,我们不想用 DOM 事件,就想触发一个我自己自定义的事件。
那么首先,我们先引入,并且实例化:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

然后接下来的 btn1,我们还需要去监听吗?压根就不需要。
我们直接找 pipe 来 addListener:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

并且我要获取到一个通知:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

可以看到,比原来的写法还要精简很多。

以及接下来,我们在 btn2 里面想触发。
我们直接 dispatch,触发一个 aaa 就可以了:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

然后我们来试试:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

可以看到,是能触发了。
当然我们也可以传参,比如我现在传了一个 12,5:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

那么我们也打印下,看看能不能出来:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

可以看到,点 按钮2 的时候,也是能触发的,并且参数也能过去。
其实跟 DOM 所带的那套事件,是一模一样的,并且比它那个还要精简很多,用起来也方便很多。

然后我们现在想要来测试一个东西,因为我们一直都在鄙视 DOM,说它性能不行。
那么我们现在完全可以试试,看看它性能到底行不行。

我们先来看看传统的 DOM 事件。
比方说,我们做个 10W 次循环,在循环里面做的就是,不停的创建事件,派发事件:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

我们取 3 次的平均值,这样更准确一些:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

那么结果是:
(381 + 369 + 383) / 3 = 377.66 ms

然后我们再用 pipe 试一次:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

同样取 3 次平均值:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

结果是:
(12 + 9 + 10) / 3 = 10.33 ms
可以看到,基本是有 36 倍的性能差。
所以说,只要跟 DOM 沾边的东西,你会发现它性能都不咋地。
那么现在这个时候,我们就已经完成了事件队列的这个过程。

然后先肯定一点,我们个人其实不太倾向去使用 addListener 和 dispatch 这么长的名字,因为用起来不是很方便。
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

所以有很多人喜欢管它们叫做 on 和 emit:
JavaScript进阶|JavaScript进阶(五十二)(自己实现一个事件队列)
文章图片

相信大家首先肯定会想到 vue,其实不是的。
基本上来说,所有的这些事件队列库都叫这 2 个名字,并不是只有 vue 专用。
那么在这种情况下,我们用这 2 个名字,就更加的精简一些。

    推荐阅读