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

news2024/9/29 19:36:00

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.coloradmin.cn/o/2177636.html

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

相关文章

【FaceFusion3.0.0】全新升级,重磅发布!

FaceFusion 3.0.0 版本引入了许多新特性和改进,其中包括: 重新设计架构,使所有操作都作为“任务”进行处理。在面部交换功能中引入像素增强(pixel boost)。向面部检测器添加多角度处理功能。引入年龄修正处理器(age modifier processor)。引…

前端学习笔记-JS进阶篇-02

构造函数&数据常用函数 1、深入对象 1.1、创建对象三种方式 1. 利用对象字面量创建对象 2. 利用new Object 创建对象 3. 利用构造函数创建对象 1.2、构造函数 构造函数:是一种特殊的函数,主要用来初始化对象 使用场景:常规的{...} 语…

MES系统实现制造业生产自动化、智能化与透明化

万界星空科技MES系统通过集成硬件和软件,实现对生产过程的实时监控、数据采集、任务调度、资源分配、质量控制、文档管理等功能,旨在优化企业的生产流程,提高生产效率,降低成本,并确保产品质量。涵盖了离散制造、流程制…

RK3588主板PCB设计学习

DCDC电路可以直接参考数据手册: 电源输出3A,回流GND也应该是3A,回流路径和输出路径的电流是一致的,不要输出路径布线很粗,GND回流路径很细,并且应该保证回流面积最小: 这一点讲的很到位&#xf…

一款基于 RBAC 的 Net8 后台管理框架,权限管理,前后台分离,支持多站点单点登录(附源码)

前言 在当今快速发展的互联网时代,一款强大的后台管理系统对于提升工作效率、简化cao作流程具有重要意义。然而,在众多开源项目中,大部分是以Java语言编写的,这对于专注于.NET平台的开发者来说,存在一定的使用门槛。 …

云+AI时代下,Alibaba Cloud Linux 如何进一步演进?

编者按:云AI 浪潮势不可挡,服务器操作系统如何进一步演进?阿里云智能集团高级技术专家张金利在 2024 云栖大会操作系统开源专场上,分享了《Alibaba Cloud Linux 技术路线演进思考》。内容见下: (图/张金利&…

MacOS安装MindSpore(2024年最新)

大家好,我是邵奈一,一个不务正业的程序猿、正儿八经的斜杠青年。 1、世人称我为:被代码耽误的诗人、没天赋的书法家、五音不全的歌手、专业跑龙套演员、不合格的运动员… 2、这几年,我整理了很多IT技术相关的教程给大家&#xff0…

项目管理系统中的风险管理:如何识别和应对项目风险?

在现代项目管理中,风险管理是确保项目成功的关键因素之一。无论是技术、资源还是市场的变化,风险无处不在。有效的风险管理能够帮助团队识别潜在问题并制定应对策略,从而避免项目延误和预算超支。项目管理系统在这一过程中扮演着重要角色&…

从 TCP Reno 经 BIC 到 CUBIC

重读 TCP拥塞控制算法-从BIC到CUBIC 以及 cubic 的 tcp friendliness 与拐点控制 这两篇文章,感觉还是啰嗦了,今日重新一气呵成这个话题。 reno 线性逼近管道容量 Wmax,相当于一次查询(capacity-seeking),但长肥管道从 0.5*Wmax …

试填+组合数学,CF 1648C - Tyler and Strings

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1648C - Tyler and Strings 二、解题报告 1、思路分析 考虑枚举相同前缀…

MySQL约束:外键约束

下面先创建两张表用来作为实验样例 1.创建dept表 create table dept(id int auto_increment comment ID primary key,name varchar(50) not null comment 部门名称 ) comment 部门表;INSERT INTO dept (id, name) VALUES (1, 研发部), (2, 市场部), (3, 财务部), (4, 销售部…

【C语言软开面经】

C语言软开面经 malloc calloc realloc free动态分配内存malloccalloc函数:realloc 函数:free函数: 堆栈-内存分区栈区(Stack):堆区(Heap):全局(静态&#xff…

哪个待办事项提醒软件推荐?待办事项提醒软件哪个合适?

在快节奏的现代生活中,我们每个人每天都会被各种待办事项所包围。从工作计划到个人生活,从学习任务到家庭琐事,这些事项往往繁杂且紧急,稍不留神就可能错过重要的截止日期。因此,选择一款合适的待办事项提醒软件&#…

Docker-2.如何保存数据退出

在使用Docker时,我们常常需要修改容器中的文件,并且希望在容器重启后这些修改能够得到保留。 0.简介 使用Docker时有一个需要注意的问题:当你修改了容器中的文件后,重启容器后这些修改将会被重置,深入研究这个问题。 …

如何高效运营知识产权,实现企业价值最大化?

知识产权的运营管理是指企业对其所拥有的专利、商标、著作权等知识产权进行规划、运用、保护和管理的一系列活动。这些活动旨在最大化知识产权的价值,促进企业的技术创新和市场竞争力的提升。通过有效的知识产权运营管理,企业可以实现技术成果的快速转化…

猝发传输和非猝发传输

猝发传输和非猝发传输是两种不同的数据传输方式,主要区别在于数据传输的连续性以及数据包的发送方式。 猝发传输 (Burst Transmission): 定义: 猝发传输是指在一段时间内,大量数据包集中发送,然后在一段时间内没有数据传输,这种…

全流程SWAP农业模型数据制备、敏感性分析及气候变化影响技术应用

SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了W…

HAL+M4学习记录_1

一、简介 基于Arm Cortex-M4的STM32F4 MCU系列采用了意法半导体的NVM工艺和ART Accelerator™,在高达180 MHz的工作频率下通过闪存执行时其处理性能达到225 DMIPS/608 CoreMark,这是迄今所有基于Cortex-M内核的微控制器产品所达到的最高基准测试分数。 这…

奖金高达 110 万元,Spatial Joy 2024 全球 AR 应用开发大赛启动

今年是AR应用开发大赛第三届,恰逢Rokid成立十周年,我们推出全新的大赛品牌“Spatial Joy”,引领开发者享受开发乐趣,为其打造充满挑战和惊喜的开发之旅,逐渐成为空间计算时代全球最大AR应用开发大赛。回顾大赛发展&…

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

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