文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

JavaScript 简单实现观察者模式和发布-订阅模式

2023-08-21 12:45

关注

1.1 什么是观察者模式

概念:观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。

如何理解这句话呢?来举个生活中的例子

学生小明情绪比较容易波动,所以当小明的情绪发生变化时,父母和老师希望及时获得通知,以便可以采取适当的措施来帮助他。

这样父母和老师就能及时了解小明的情绪状态,当小明情绪低落时,他们可以给予他关心、安慰和支持。
在这个例子中,小明就是被观察者,而父母和老师都是观察者。

1.2 代码实现

观察者模式有何应用呢?

Vue的响应式就是基于观察者模式的,下面就来简单实现一下它的代码。

// 被观察者 学生class Subject {  constructor() {    this.state = "happy";    this.observers = []; // 存储所有的观察者  }  //新增观察者  add(o) {    this.observers.push(o);  }  //获取状态  getState() {    return this.state;  }  // 更新状态并通知  setState(newState) {    this.state = newState;    this.notify();  }  //通知所有的观察者  notify() {    this.observers.forEach((o) => o.update(this));  }}// 观察者 父母和老师class Observer {  constructor(name) {    this.name = name;  }  //更新  update(student) {    console.log(`亲爱的${this.name} 通知您当前学生的状态是${student.getState()}`);  }}let student = new Subject();let parent = new Observer("父母");let teacher = new Observer("老师");//添加观察者student.add(parent);student.add(teacher);//设置被观察者的状态student.setState("sad");

在这里插入图片描述

2.1 什么是发布-订阅模式

发布订阅模式跟观察者模式很像,它们其实都有发布者订阅者,但是他们是有区别的:

为了更好区分这两种设计模式,接着上述例子。

通过发布订阅模式,小明不需要直接告诉每位老师他的情绪状态,而是通过情绪监测系统自动发布消息给所有订阅了他情绪状态的老师。这种发布者不直接接触到订阅者的模式,就是发布订阅模式。
在这里插入图片描述
那么发布订阅模式有何应用呢?
Vue的EventBus事件总线其实就是用了发布订阅模式。用法如下:
1.创建全局事件总线

// main.jsimport Vue from "vue"Vue.prototype.$bus = new Vue()

通过on订阅事件

//组件Aexport default{    mounted(){        // 监听事件的触发        this.$bus.$on("sendMsg", data => {            console.log(data)//身体健康        })    },    beforeDestroy(){        // 取消监听        this.$bus.$off("sendMsg")    }}

通过emit发布事件

//组件B<template>    <button @click="handlerClick">点击发送数据</button></template>export default{    methods:{        handlerClick(){            this.$bus.$emit("sendMsg", "身体健康")        }    }}

了解了EventBus的使用后,那么接下来就来手动实现一个EventBus。

2.2 代码实现

2.2.1 基础版

实现目标:使用 $on 订阅事件,使用 $emit 发布事件。
主要思路:

class EventBus {  constructor() {    // 缓存列表,用来存放注册的事件与回调    this.cache = {};  }  // 订阅事件  on(name, cb) {    // 如果当前事件没有订阅过,就给事件创建一个队列    if (!this.cache[name]) {      this.cache[name] = []; //由于一个事件可能注册多个回调函数,所以使用数组来存储事件队列    }    this.cache[name].push(cb);   }  // 触发事件  emit(name, ...args) {    // 检查目标事件是否有监听函数队列    if (this.cache[name]) {      // 逐个调用队列里的回调函数      this.cache[name].forEach((callback) => {        callback(...args);      });    }  }}// 测试let eventBus = new EventBus();// 订阅事件eventBus.on("teacherName1", (pos, state) => {  console.log(`订阅者小陈老师,小明同学当前在${pos},心情状态是${state}`);});eventBus.on("teacherName1", (pos, state) => {  console.log(`订阅者小陈老师,小明同学当前在${pos},心情状态是${state}`);});eventBus.on("teacherName2", (pos, state) => {  console.log(`订阅者小李老师,小明同学当前在${pos},心情状态是${state}`);});// 发布事件eventBus.emit("teacherName1", "教室", "伤心");eventBus.emit("teacherName2", "操场", "开心");

输出结果:

在这里插入图片描述

2.2.2 取消订阅

实现目标:增加 off 方法取消订阅。

class EventBus {  constructor() {    // 缓存列表,用来存放注册的事件与回调    this.cache = {};  }  // 订阅事件  on(name, cb) {    // 如果当前事件没有订阅过,就给事件创建一个队列    if (!this.cache[name]) {      this.cache[name] = []; //由于一个事件可能注册多个回调函数,所以使用数组来存储事件队列    }    this.cache[name].push(cb);   }  // 触发事件  emit(name, ...args) {    // 检查目标事件是否有监听函数队列    if (this.cache[name]) {      // 逐个调用队列里的回调函数      this.cache[name].forEach((callback) => {        callback(...args);      });    }  }  // 取消订阅  off(name, cb) {    const callbacks = this.cache[name];     const index = callbacks.indexOf(cb);     if (index !== -1) {      callbacks.splice(index, 1);     }  }}// 测试let eventBus = new EventBus();let event1 = function (...args) {  console.log(`通知1-订阅者小陈老师,小明同学当前心情状态:${args}`)};let event2 = function (...args) {  console.log(`通知2-订阅者小陈老师,小明同学当前心情状态:${args}`)};// 订阅事件eventBus.on("teacherName1", event1);eventBus.on("teacherName1", event2);// 取消订阅事件1eventBus.off('teacherName1', event1);// 发布事件eventBus.emit("teacherName1", "教室", "上课", "打架", "愤怒");eventBus.emit("teacherName2", "教室", "上课", "打架", "愤怒");

输出结果:

在这里插入图片描述

2.2.3 订阅一次

实现目标:增加 once 方法只订阅一次。

class EventBus {  constructor() {    // 缓存列表,用来存放注册的事件与回调    this.cache = {};  }  // 订阅事件  on(name, cb) {    // 如果当前事件没有订阅过,就给事件创建一个队列    if (!this.cache[name]) {      this.cache[name] = []; //由于一个事件可能注册多个回调函数,所以使用数组来存储事件队列    }    this.cache[name].push(cb);   }  // 触发事件  emit(name, ...args) {    // 检查目标事件是否有监听函数队列    if (this.cache[name]) {      // 逐个调用队列里的回调函数      this.cache[name].forEach((callback) => {        callback(...args);      });    }  }  // 取消订阅  off(name, cb) {    const callbacks = this.cache[name];     const index = callbacks.indexOf(cb);     if (index !== -1) {      callbacks.splice(index, 1);     }  }  // 只订阅一次  once(name, cb) {    // 执行完第一次回调函数后,自动删除当前订阅事件    const fn = (...args) => {      cb(...args);       this.off(name, fn);     };    this.on(name, fn);  }}// 测试let eventBus = new EventBus();let event1 = function (...args) {  console.log(`通知1-订阅者小陈老师,小明同学当前心情状态:${args}`)};// 订阅事件,只订阅一次eventBus.once("teacherName1", event1);// 发布事件eventBus.emit("teacherName1", "教室", "上课", "打架", "愤怒");eventBus.emit("teacherName1", "教室", "上课", "打架", "愤怒");eventBus.emit("teacherName1", "教室", "上课", "打架", "愤怒");

输出结果:

在这里插入图片描述

写作不易,你的一赞一评,就是我前行的最大动力。如有问题,欢迎指出!

来源地址:https://blog.csdn.net/weixin_43288600/article/details/131968091

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯