JS设计模式之观察者模式:观察者与可观察对象的巧妙互动

image.png

一. 前言

在前端开发中,我们经常会遇到需要对用户的操作进行响应的场景,例如页面上的按钮点击输入框内容变化等。为了实现这种响应式的设计,我们可以使用观察者模式来解耦各个组件之间的依赖关系。

本文将详细介绍观察者模式的原理和实现方法,并通过实例代码演示如何使用观察者模式来实现一个简单的响应式系统。我们将从以下几个方面展开分析:

  1. 基本定义和核心概念

  2. 如何实现观察者模式

  3. 在前端开发中的应用场景

  4. 使用注意事项

希望通过本文的学习,可以深入了解观察者模式的用法,并将其应用于实际开发中,提高代码的质量。

二. 什么是观察者模式

1. 定义

观察者模式Observer Pattern)是一种行为型设计模式,它定义了一种一对多依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有观察者都会收到通知并进行相应的处理。在前端开发中,观察者模式经常用于实现事件监听和响应式系统。

在 JavaScript 中,我们可以使用以下步骤来实现观察者模式:

  1. 定义一个主题(Subject)对象,它包含状态和添加、删除观察者的方法;

  2. 定义一个观察者(Observer)对象,它包含更新方法,用于在主题状态改变时更新自身的状态;

  3. 在主题对象中添加观察者对象,并通知它们状态已改变。

JavaScript 中观察者模式通常由两个主要部分组成观察者(Observers)和主题(Subject)。观察者模式被广泛应用于前端开发中,用来实现事件驱动的程序设计,允许主题对象和依赖于它的观察者对象之间的松散耦合。

image.png

下面是观察者模式的基本结构示例:

// 观察者
class Observer {update(data) {// 观察者接收到主题的更新通知后执行的操作console.log("Received update:", data);}
}// 主题
class Subject {constructor() {this.observers = []; // 存储观察者的数组}// 添加观察者addObserver(observer) {this.observers.push(observer);}// 通知所有观察者更新notify(data) {this.observers.forEach((observer) => {observer.update(data);});}
}// 实例化观察者和主题
const observer1 = new Observer();
const observer2 = new Observer();const subject = new Subject();// 将观察者添加到主题中
subject.addObserver(observer1);
subject.addObserver(observer2);// 主题通知所有观察者更新
subject.notify("Hello, observers!");

在上述示例中,观察者通过 update 方法来处理接收到的主题更新。主题维护了一个观察者数组,并提供了添加观察者和通知观察者的方法。

这是观察者模式的简单实现,它演示了主题和观察者之间的松耦合关系,当主题发生改变时,所有观察者都能够接收到通知并做出相应的处理。

2. 核心概念

JavaScript 观察者模式的核心概念主要包括以下几点:

  1. 主题Subject):主题是被观察的对象,它会维护一个观察者列表,并提供方法来添加或删除观察者,以及通知观察者更新的功能。

  2. 观察者Observer):观察者是订阅主题的对象,它会在主题状态发生变化时接收到通知,并执行相应的更新操作。

  3. 订阅与发布:观察者模式通过订阅与发布的机制实现,主题的状态变化会通知所有观察者,而观察者并不需要直接了解主题的实现细节,实现了解耦。

  4. 一对多关系:在观察者模式中,一个主题可以拥有多个观察者,这使得主题状态的变化可以同时通知到多个观察者对象。

  5. 松耦合:观察者模式实现了主题和观察者之间的松耦合,主题不需要知道观察者的具体细节,观察者也不需要了解主题内部实现,达到了对象间的解耦。

总的来说,观察者模式的核心概念是通过主题和观察者之间的订阅与发布机制,实现了一对多的关系,让多个观察者能够响应并处理主题的状态变化,从而实现了对象间的松耦合。JavaScript 观察者模式在前端开发中得到广泛应用,例如在处理用户界面事件、数据绑定、状态管理等方面发挥着重要作用。

三. 如何实现观察者模式

JavaScript 观察者模式的详细实现步骤如下:

1. 创建主题对象

首先需要创建一个主题对象,它需要包含以下功能:

  • 存储观察者列表的数据结构(一般为数组或者其他适合存储对象的数据结构)。

  • 提供 subscribe 方法用于观察者订阅(注册)主题。

  • 提供 unsubscribe 方法用于观察者取消订阅。

  • 提供 notify 方法用于在主题状态变化时通知所有订阅的观察者。

2. 创建观察者对象

创建一个或多个观察者对象,观察者需要包含以下功能:

  • update 方法或者其他类似方法,用于在接收到主题状态变化通知时执行相应的操作。

3. 订阅和发布

观察者通过调用主题对象的 subscribe 方法来订阅主题,使得自身可以接收主题的状态变化通知;当主题状态发生变化时,主题对象通过调用所有订阅的观察者对象的 update 方法来通知它们。

4. 可选:取消订阅

如果观察者不再对主题的状态变化感兴趣,可以调用主题对象的 unsubscribe 方法取消订阅。

通过面的步骤,可以实现一个简单的观察者模式的示例代码:

// 创建主题对象
const subject = {observers: [],subscribe: function (observer) {this.observers.push(observer);},unsubscribe: function (observer) {this.observers = this.observers.filter((item) => item !== observer);},notify: function (data) {this.observers.forEach((observer) => observer.update(data));},
};// 创建观察者对象
const observer1 = {update: function (data) {console.log("Observer 1 received: " + data);},
};const observer2 = {update: function (data) {console.log("Observer 2 received: " + data);},
};// 订阅和发布
subject.subscribe(observer1);
subject.subscribe(observer2);
subject.notify("Hello, observers!"); // 所有观察者都会收到通知

四. 应用场景

观察者模式在前端开发中有很多应用场景,可以用于处理事件的订阅和通知数据绑定模块间通信等情况。下面我将结合代码示例说明几个常见的应用场景。

1. 事件订阅与通知

观察者模式可以用于处理前端事件的订阅和通知,如按钮点击、键盘输入等。

// 创建一个按钮作为主题对象
const button = document.querySelector("#myButton");// 创建观察者对象
const clickHandler = {update: function (event) {console.log("Button clicked!");// 执行相应的操作},
};// 订阅按钮的点击事件
button.addEventListener("click", function (event) {clickHandler.update(event);
});

在上面的例子中,按钮被作为主题对象,观察者对象 clickHandler 订阅了按钮的点击事件,当按钮被点击时,观察者对象收到通知并执行相应的操作。

2. 数据绑定

观察者模式可以用于实现数据模型和视图之间的双向绑定,通知视图更新数据的变化。

// 创建数据模型作为主题对象
const dataModel = {data: 'Hello, observer pattern!'
};// 创建观察者对象
const dataUpdater = {update function(newValue) {// 更新视图显示document.getElementById('output').textContent = newValue;}
};// 订阅数据模型的变化
dataModel.subscribe(dataUpdater);// 当数据模型发生变化时,通知观察者对象更新视图
dataModel.data = 'New data has arrived!';
dataModel.notify();

在上面的例子中,数据模型 dataModel 被作为主题对象,观察者对象 dataUpdater 订阅了数据模型的变化,并在数据变化时收到通知并更新视图。

3. 模块间通信

观察者模式可以用于模块间的消息订阅与通知,实现模块之间的解耦合通信。

// 创建一个全局的事件总线作为主题对象
const eventBus = {observers: {},subscribe: function(eventName, observer) {if (!this.observers[eventName]) {this.observers[event] = [];}this.observers[eventName].push(observer);},notify: function(eventName, data) {if (thiservers[eventName]) {this.observers[event].forEach(observer => observer.update(data));}}
};// 创建两个模块作为观察者对象
const module1 = {update: function(data) {console.log('Module 1 received data: ' data);// 执行相应的操作}
};const module2 = {update: function(data) {console.log('Module 2 received data: ' + data);// 执行相应的操作}
};// 订阅事件并在事件发生时收到通知
eventBus.subscribe('dataChange', module1);
eventBus.subscribe('dataChange', module2);
eventBus.notify('dataChange', 'Some new data has arrived!');

在上面的例子中,eventBus 充当了一个全局的事件总线,模块 module1module2 作为观察者对象订阅了事件,并在事件发生时收到通知并执行相应的操作。

4. 数据驱动视图更新

Vue 框架和 React 框架都使用了观察者模式的概念。

在 Vue 框架中,Vue 实例对象包含了一个响应式系统,当数据发生变化时,视图会自动更新。这种响应式系统就是基于观察者模式实现的。

在 React 框架中,虽然普遍使用了单向数据流的概念,但在内部也使用了观察者模式。比如,React 中的组件可以订阅数据源的变化,一旦数据源发生变化,组件就会收到通知并及时更新。这种机制与观察者模式的概念是一致的。

因此,可以说 Vue 框架和 React 框架都使用了观者模式的概念,虽然具体的实现细节和背的原理可能所不同,但它们是基于观察者模式来实现驱动视图更新的。

五. 注意事项

在使用观察者模式时,有几个注意事项需要考虑:

1. 内存泄漏

当观察者和主题对象的引用关系不当时,可能会产生内存泄漏。例如,如果观察者对象在不再需要时未能取消订阅主题对象,将导致观察者对象无法被销毁,从而导致内存泄漏。因此,在不再需要观察者对象时,应当及时取消订阅。

2. 避免循环引用

当主题对象和观察者对象之间发生循环引用时,也会导致内存泄漏。因此,在设计观察者模式时,需要小心避免产生循环引用,或者采取一些特殊的处理手段来解决循环引用问题。

3. 性能考虑

使用观察者模式时需要考虑性能问题。特别是在大规模数据变更时可能导致大量的通知操作,这可能会影响性能。因此在实际应用中需要慎重考虑使用观察者模式,避免性能问题。

总的来说,在使用JavaScript观察者模式时,需要注意内存泄漏、循环引用、性能、过度使用等问题,同时保持清晰的命名和接口,以便维护和扩展。

六. 结语

在 JavaScript 中深入了解观察者模式,我们可以看到观察者模式的强大应用。通过观察者模式,我们可以实现模块间的解耦合、事件订阅和通知、数据绑定等功能,提高代码的可维护性和拓展性。

然而,观察者模式也需要小心使用。在实际应用中,需要考虑内存泄漏、循环引用、性能和过度使用等问题。合适的使用观察者模式可以帮助我们优化代码结构和模块间通信,提高应用的可维护性和可扩展性。

最后,我希望在实际项目中,大家能够根据具体场景合理选择是否使用观察者模式,避免过度设计。同时,也希望大家在使用观察者模式时,注意规范命名和接口,以便让其他开发人员易于理解和使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1550384.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

使用【apifox】进行压测-保姆级教程【无需脚本】

1.根据接口文档进行测试,写一个接口,能够调通即可 2.选择“从接口导入”,选择刚刚测试的接口 3.选择一个环境,我这里用的云服务器http://x.xx.xxx.xx (端口号写不写都行,我是加上了) 4.选性…

element-ui 通过按钮式触发日期选择器

element ui 写在前面1. 自定义的日期时间组件CustomDatePicker.vue2. 页面效果总结写在最后 写在前面 需求:elementui中日期时间选择器,目前只能通过点击input输入框触发日期选择器,我希望能通过其他方式触发日期选择器同时把input输入框去掉…

【IoT-NTN】系统消息SIB32信令分析

3GPP卫星通信发展迅速, TS36.331 R17中新增SIB32携带星历信息,本文对SIB32的信令内容进行了分析。 SystemInformationBlockType32 概述 SystemInformationBlockType32 是用于提供预测非连续覆盖的卫星辅助信息的系统信息块。这个信息块仅在非地面网络&…

初学者如何快速入门Python(详细攻略),从0到精通,不信你学不会!

近年来,人工智能领域的飞速发展极大地改变了各个行业的面貌。当前最新的技术动态,如大型语言模型和深度学习技术的发展,展示了深度学习和机器学习技术的强大潜力,成为推动创新和提升竞争力的关键。特别是PyTorch,凭借其…

刚面试完的前端面试题

今天晚上参加了一场长达40多分钟的技术面。我觉得面试官非常专业,问的问题也都是很棒的!自己很多知识都需要学习。所以我决定回想并记录下来。回答不对的地方欢迎大家指正! 我自己在小本本上回忆出来的大概就是26道题。后期我会持续更新我学习…

【测试-BUG篇】软件测试的BUG知识你了解多少呢?

文章目录 1. 软件测试的生命周期2. BUG3. BUG的生命周期4. 与开发人员起争执怎么办 1. 软件测试的生命周期 🍎软件测试 贯穿整个软件的生命周期; 🍎软件测试的生命周期是指测试流程; ①需求分析 用户角度:软件需求是…

资源管理5步法:优化你的项目管理

作为项目经理,负责交付项目是一项复杂且要求严格的任务。在缺乏必要的专业知识、工具、设备以及资金支持的情况下,成功完成项目几乎是不可能的。 因此,项目资源的规划与分配是项目启动前至关重要的环节。若未能妥善规划,将可能导…

测试卡(1)灰卡

#灵感# 灰卡为什么是18%?文章分为三部分,前部分,解释灰卡的定义,后部分是 市场买的18%灰卡的说明书,其中穿插了网络上搜到的灰卡使用案例。 目录 18% 中性灰卡应用说明 1) 曝光水平 例子:用灰…

有些硬盘录像机接入视频汇聚平台EasyCVR后通道不显示/显示不全,该如何处理?

EasyCVR视频监控汇聚管理平台是一款针对大中型项目设计的跨区域网络化视频监控集中管理平台。该平台不仅具备视频资源管理、设备管理、用户管理、运维管理和安全管理等功能,还支持多种主流标准协议,如GB28181、RTSP/Onvif、RTMP、部标JT808、GA/T 1400协…

初识Linux以及Linux的基本命令

千呼万唤始出来,Linux系列的文章从今天起开始不定期更新,闲话少叙,我们直接进入正题 初识Linux 这一部分我不打算给大家讲Linux的发展史啥的,直接从系统方面开始介绍 首先,我们平时用win10或win11所看到的桌面以及各…

Pycharm 本地搭建 stable-diffusion-webui

一、下载工程源码 Github官方连接 https://github.com/AUTOMATIC1111/stable-diffusion-webui 二、Pycharm打开工程 1、设置环境 文件-设置-项目-Python解析器-添加解释器-添加本地解释器 Conda环境-创造新环境-Python版本3.10 注意一定要选择Python3.10版本,否…

985官宣:19名本科生,获国自然项目!

9月24日,据复旦大学教务处消息,国家自然科学基金委公布了2024年国家自然科学基金青年学生基础研究项目(本科生)立项情况,复旦大学共有19名基础学科专业本科生获得国家自然科学基金委资助。 此前,据武汉大学…

浮点数的这些特性你了解吗

问题1:下面的代码,输出结果是什么: public class CaclTest{public void test1(){float f 1.0F / 0.0F;System.out.println("f:" f)}public static void main(String[] args){CaclTest ct new CaclTest();ct.test1();}} A. 运行抛出异常:j…

vue3监听子组件的生命周期

1.Vue3使用vue&#xff0c;vue2使用hook template:<compG vue:mounted"doSomething"></compG>script://监听子组件生命周期let doSomething (e: any) > {console.log("没有啊11", e);}; 2.打印结果

誉天Linux云计算课程学什么?为什么保障就业?

一个IT工程师相当于干了哪些职业? 其中置顶回答生动而形象地描绘道&#xff1a; 一个IT工程师宛如一个超级多面手&#xff0c;相当于——加班狂程序员测试工程师实施工程师网络工程师电工装卸工搬运工超人。 此中酸甜苦辣咸&#xff0c;相信很多小伙伴们都深有体会。除了典…

macOS 开发环境配置与应用开发

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【LeetCode】动态规划—931. 下降路径最小和(附完整Python/C++代码)

动态规划—931. 下降路径最小和 前言题目描述基本思路1. 问题定义2. 理解问题和递推关系3. 解决方法3.1 动态规划方法3.2 空间优化的动态规划 4. 进一步优化4.1 空间复杂度优化 5. 小总结 代码实现Python3代码实现Python 代码解释C代码实现C 代码解释 总结: 前言 在算法的学习…

MWORKS.Sysplorer 2024b重磅推出同元基础库

一、引言 MWORKS.Sysplorer 是多领域建模与仿真平台&#xff0c;集成了Modelica标准库。该库由Modelica协会开发&#xff0c;是一款开源的通用基础模型库&#xff0c;支持机电、流体、控制等多个专业领域的建模与仿真。随着Modelica标准库的不断发展与更新&#xff0c;目前最新…

【设计模式-中介者模式】

定义 中介者模式&#xff08;Mediator Pattern&#xff09;是一种行为设计模式&#xff0c;通过引入一个中介者对象&#xff0c;来降低多个对象之间的直接交互&#xff0c;从而减少它们之间的耦合度。中介者充当不同对象之间的协调者&#xff0c;使得对象之间的通信变得简单且…

双十一不被割韭菜!2024双十一总结五款好物分享!

每年双十一购物节来临之际&#xff0c;随着心仪商品缓缓填满购物车&#xff0c;那份对即将收获的期待与内心的喜悦&#xff0c;自然而然地溢于言表。在这个优惠纷呈的购物盛宴中&#xff0c;寻找那些既符合个人需求又具备高品质的宝贝&#xff0c;成为了一项既充满乐趣又考验眼…