UzumakiItachi
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

UzumakiItachi

起风了,唯有努力生存。
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • JavaScript

    • 手撕Promise
    • 手撕JS八股文系列
    • 手撕观察者模式和发布订阅模式
      • 前言
      • 发布订阅模式
        • 先大致的分析一下什么是发布订阅模式
      • 观察者模式
      • 总结
    • 页面可见性——visibilitychange
    • JS数据类型那点事儿
    • 作用域、执行上下文和this
    • 防抖和节流
    • 装饰器
    • JS设计模式小结
  • CSS

  • 前端
  • JavaScript
hanhanbuku
2023-03-03
目录

手撕观察者模式和发布订阅模式

# 前言

提示

在js中发布订阅模式和观察者模式都是非常常用的两种设计模式。两者也及其相似,初学者很容易搞混了两种设计模式,下面通过手写的方式带大家完整的认识这两种设计模式以及他们之间的区别

# 发布订阅模式

# 先大致的分析一下什么是发布订阅模式

可以参考一下微信的公众号订阅功能,用户订阅了这个公众号,公众号有了新消息就会推送给用户。在这样一个流程中,大致就可以分出三个角色来 用户充当的就是订阅者而公众号则是发布者,那第三个角色是谁呢? 第三个角色当然就是公众号的数据来源啦,这个数据来源可以看成一个中转站。他并不关心谁订阅了消息,订阅了什么消息,他要做的就是存下订阅者的联系方式,当对应的消息被发布了他就通知订阅者。

知道了他的大概思想之后就可以勾勒出代码的大致轮廓了

//发布订阅容器
let observeEmit = {
 eventList: {},//装载订阅者所订阅的事件
//订阅函数
 on(key, fn) {},
//发布函数
 emit(key, paylod) {}
}
1
2
3
4
5
6
7
8

上述代码中eventList就是用来存储订阅者的联系方式的,on方法用来提供给用户进行订阅emit用来发布

这两个函数的具体实现如下

        //订阅函数
        on(key, fn) {
            /**
             * key  被订阅的内容的key值
             * fn 订阅者传入的订阅事件
             */
            if (!this.eventList[key]) {
                this.eventList[key] = []
            }
            this.eventList[key].push(fn)
        },
        //发布函数
        emit(key, paylod) {
                for (let i = 0; i < this.eventList[key].length; i++) {
                    if (this.eventList){
                        this.eventList[key][i](paylod)
                    }
                }
        },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

至此,一个简单的发布订阅模式就写好了,主要就是在订阅的时候将key以及对应的回调函数存入容器里,然后在发布某个消息的时候去触发所有的对应的key的回调函数。 下面看看如何使用吧

    observeEmit.on('msg', function (content) {
        console.log('订阅msg的人收到最新消息:', content)
    })
   observeEmit.emit('msg', '我触发了发布,请问订阅者收到了吗')
1
2
3
4

这还只是一个最简单版的,我们还可以对他进行功能扩展

比如,提供只订阅一次的api

        //只订阅一次
        once(key, fn) {
            this.on(key, function (content) {
                fn(content)
                this.off(key, fn)
            })
        },
        //取消订阅
        off(key, fn) {
            if (this.eventList[key]) {
                for (let i = 0; i < this.eventList[key].length; i++) {
                    if (this.eventList[key][i] === fn) {
                        this.eventList[key][i].splice(i, 1)
                    }
                }
            }
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

如果先发布了消息,用户再订阅,我们还可以将事件存储起来,然后在有人订阅的时候将他发布出去

在对象中定义一个离线事件的属性
        offLineEventList: {},//离线事件栈,如果当前发布的事件暂无订阅者,就先保存到此事件栈中,等下次有人订阅了再将此消息发布给他

改造一下on和emit
//订阅函数
        on(key, fn) {
            /**
             * key  被订阅的内容的key值
             * fn 订阅者传入的订阅事件
             */
            if (!this.eventList[key]) {
                this.eventList[key] = []
            }
            this.eventList[key].push(fn)
            //如果离线数据栈中有当前订阅的key,那就将之前发布的消息发送给订阅者
            if (this.offLineEventList[key]) {
                this.emit(key, this.offLineEventList[key])
            }
        },
        //发布函数
        emit(key, paylod) {
            console.log(arguments)
            if (this.eventList[key]) {
                for (let i = 0; i < this.eventList[key].length; i++) {
                    if (this.eventList){
                        this.eventList[key][i](paylod)
                    }
                }
            } else {
                //如果没人订阅过这个事件,就将本次发布内容先存在离线事件栈内
                this.offLineEventList[key] = paylod
            }
        },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

这样一个较为完善的发布订阅模式就写好啦。 接下来我们就来看看观察者模式又是怎么样的吧

# 观察者模式

观察者模式其实可以简单的分为两个角色,一个观察者和一个被观察者。由被观察者主动通知观察者,告诉他我发生了变化,你可以做你想做的事情了

下面简单实现一下

    //观察者类
    class Observe {
        constructor(name) {
           this.name = name
        }
        //观察的数据发生了变化,触发函数
        update(payload){
            console.log(`${this.name}观察的数据发生了变化:${payload}`)
        }
    }
    //被观察的类
    class Subject {
        constructor() {
            this.observeList = []
        }
        //添加观察者进观察者数组
        addObserve(observe){
            this.observeList.push(observe)
        }
        //发生变更通知观察者
        notify(payload){
            this.observeList.forEach(observe=>{
                observe.update(payload)
            })
        }
    }
    const subject = new Subject()
    const stu1 = new Observe('小明')
    const stu2 = new Observe('小虹')
    subject.addObserve(stu1)
    subject.addObserve(stu2)
    subject.notify('测试一下')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 总结

提示

发布订阅模式中有三个角色,而观察者模式中只有两个角色。 发布订阅模式由中转站收集订阅者的订阅消息,然后发布者通过中转站来通知到对应的订阅者, 而观察者模式则是有被观察者主动收集他自身的观察者,然后自身发生变化后主动的去通知观察者,也就是调用观察者的某个函数。两者的概念非常相似,但是实现却大有不同

编辑 (opens new window)
上次更新: 2023/03/08, 09:35:38
手撕JS八股文系列
页面可见性——visibilitychange

← 手撕JS八股文系列 页面可见性——visibilitychange→

最近更新
01
小程序实现全局状态管理
07-09
02
前端检测更新,自动刷新网页
06-09
03
swiper渲染大量数据的优化方案
06-06
更多文章>
Theme by Vdoing | Copyright © 2023-2025 UzumakiItachi | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式