JavaScript中的Observer模式:设计模式与最佳实践

news2024/11/26 11:13:17

前言

在现代软件开发中,Observer模式(观察者模式)是一种重要的设计模式,能够有效地管理对象之间的依赖关系。它允许一个对象在状态发生变化时通知其他依赖于它的对象,这种一对多的依赖关系在事件驱动编程、数据绑定以及状态管理等领域非常常见。
本文将详细探讨JavaScript中几种常见的Observer实现方式,并通过具体实例展示其使用方法,帮助开发者在实际项目中更好地应用这一模式。

什么是Observer模式?

Observer模式是一种设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当这个主题对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。
想象一下,你订阅了一份报纸,每当有新的一期报纸出版时,你都会收到通知。这就是Observer模式的工作原理。

JavaScript中的常见Observer

在JavaScript中,常见的Observer模式实现方式包括:

  1. EventEmitter(事件发射器)
  2. Proxy(代理)
  3. MutationObserver
  4. IntersectionObserver

1. EventEmitter

EventEmitter是Node.js中实现Observer模式的一种方式。它允许你定义事件和事件处理程序,并在事件发生时通知所有的订阅者。

使用实例:

const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
  console.log('an event occurred!');
});

myEmitter.emit('event');
// 输出: an event occurred!

2. Proxy

Proxy对象可以用来定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。它可以用来创建观察对象的变化。

使用实例:

const handler = {
  set(target, property, value) {
    console.log(`Setting value ${value} to ${property}`);
    target[property] = value;
    return true;
  }
};

const obj = new Proxy({}, handler);
obj.name = 'John';
// 输出: Setting value John to name

3. MutationObserver

MutationObserver用于监听DOM树的变化。例如,添加或删除节点、属性变化等。

使用实例:

const targetNode = document.getElementById('someElement');
const config = { attributes: true, childList: true, subtree: true };

const callback = function(mutationsList, observer) {
  for(let mutation of mutationsList) {
    if (mutation.type === 'childList') {
      console.log('A child node has been added or removed.');
    }
    else if (mutation.type === 'attributes') {
      console.log('The ' + mutation.attributeName + ' attribute was modified.');
    }
  }
};

const observer = new MutationObserver(callback);
observer.observe(targetNode, config);

// Later, you can stop observing
observer.disconnect();

4. IntersectionObserver

IntersectionObserver用于异步观察目标元素与其祖先元素或顶级文档视窗的交叉状态,可以有效地实现懒加载图片等功能。

使用实例:

const img = document.querySelector('img');

const callback = (entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('Image is in view!');
      // 停止观察
      observer.disconnect();
    }
  });
};

const options = {
  root: null, // 默认为浏览器视窗
  rootMargin: '0px',
  threshold: 1.0 // 完全可见时触发回调
};

const observer = new IntersectionObserver(callback, options);
observer.observe(img);

5. 自定义事件系统

在现代前端开发中,我们有时需要创建自定义事件系统来管理复杂的状态变化和组件间的通信。通过手动实现一个简单的事件系统,可以更好地理解Observer模式的核心思想。

自定义事件系统实例:

class EventBus {
  constructor() {
    this.events = {};
  }

  // 订阅事件
  on(event, listener) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(listener);
  }

  // 取消订阅
  off(event, listenerToRemove) {
    if (!this.events[event]) return;
    this.events[event] = this.events[event].filter(listener => listener !== listenerToRemove);
  }

  // 触发事件
  emit(event, payload) {
    if (this.events[event]) {
      this.events[event].forEach(listener => listener(payload));
    }
  }
}

// 使用实例
const eventBus = new EventBus();

function onFoo(payload) {
  console.log(`foo has been triggered with payload: ${payload}`);
}

eventBus.on('foo', onFoo);

eventBus.emit('foo', 'Hello World!');
// 输出: foo has been triggered with payload: Hello World!

eventBus.off('foo', onFoo);

eventBus.emit('foo', 'Hello Again!');
// 无输出,因为监听器已被移除

应用场景

1. 数据绑定

在现代前端框架如Vue.js和React中,Observer模式被大量应用于数据绑定。当模型中的数据变化时,视图会自动更新。

Vue.js中的示例:

const vm = new Vue({
  data: {
    message: 'Hello Vue!'
  }
});

// 这里的vm.message是响应式的,任何对它的修改都会触发视图更新
vm.message = 'Hello again, Vue!';

2. Redux中的状态管理

在Redux中,subscribe和dispatch方法实现了基本的Observer模式。每当状态变化时,订阅者都会收到通知。

Redux中的示例:

import { createStore } from 'redux';

const reducer = (state = { count: 0 }, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    default:
      return state;
  }
};

const store = createStore(reducer);

store.subscribe(() => {
  console.log('State changed:', store.getState());
});

store.dispatch({ type: 'INCREMENT' });
// 输出: State changed: { count: 1 }

扩展:结合其他模式使用

Observer模式通常和其他设计模式一起使用,以构建更灵活和可维护的系统。例如,结合发布-订阅模式(Publish-Subscribe Pattern),可以在更大规模的应用中管理事件和状态。

发布-订阅模式:

class PubSub {
  constructor() {
    this.topics = {};
  }

  subscribe(topic, listener) {
    if (!this.topics[topic]) {
      this.topics[topic] = [];
    }
    this.topics[topic].push(listener);
  }

  unsubscribe(topic, listenerToRemove) {
    if (!this.topics[topic]) return;
    this.topics[topic] = this.topics[topic].filter(listener => listener !== listenerToRemove);
  }

  publish(topic, payload) {
    if (this.topics[topic]) {
      this.topics[topic].forEach(listener => listener(payload));
    }
  }
}

// 使用示例
const pubSub = new PubSub();

function onMessageReceived(message) {
  console.log(`Message received: ${message}`);
}

pubSub.subscribe('message', onMessageReceived);

pubSub.publish('message', 'Hello PubSub!');
// 输出: Message received: Hello PubSub!

pubSub.unsubscribe('message', onMessageReceived);

pubSub.publish('message', 'This will not be logged.');
// 无输出,因为监听器已被移除

总结

Observer模式在JavaScript中的应用非常广泛,从Node.js的事件驱动模型到前端的DOM变化监听,再到性能优化的懒加载,Observer模式都发挥了重要作用。通过EventEmitter、Proxy、MutationObserver、IntersectionObserver和自定义事件系统等机制,我们可以实现各种复杂的功能和效果。在实际开发中,合理运用Observer模式可以大大提高代码的可维护性和扩展性。

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

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

相关文章

Qt中2D绘制系统

目录 一、Qt绘制系统 1.1Qt绘制基本概念 1.2 绘制代码举例 1.3画家 1.3.1 QPainter的工作原理: 1.3.2 自定义绘制饼状图: 1.4画笔和画刷 1.4.1画笔 1.4.2 画刷填充样式 1.5 反走样和渐变 1.6绘制设备 1.7坐标变换 1.8QPainterPath 1.9绘制文…

Linux——Uboot命令使用

什么是Uboot? 1)Uboot是一个裸机程序,比较复杂。类似我们PC机的BIOS程序。 2)Uboot就是一个bootloader,作用就是用于启动Linux或者其他系统,Uboot最主要的工作是初始化DDR,因为Linux的运行是运行…

2024智能机器人与自动控制国际学术会议 (IRAC 2024)

主办,承办,支持单位 会议官网 www.icirac.org 大会时间:2024年11月29-12月1日 大会简介 2024智能机器人与自动控制国际学术会议 (IRAC 2024)由华南理工大学主办,会议将于2024年11月29日-12月1日在中国广…

Linux网络——NAT/代理服务器

一.NAT技术 1.NAT IP转换 之前我们讨论了, IPv4 协议中, IP 地址数量不充足的问题,NAT 技术就是当前解决 IP 地址不够用的主要手段, 是路由器的一个重要功能。 NAT 能够将私有 IP 对外通信时转为全局 IP. 也就是一种将私有 IP 和全局IP 相互转化的技术方法: 很…

【架构】主流企业架构Zachman、ToGAF、FEA、DoDAF介绍

文章目录 前言一、Zachman架构二、ToGAF架构三、FEA架构四、DoDAF 前言 企业架构(Enterprise Architecture,EA)是指企业在信息技术和业务流程方面的整体设计和规划。 最近接触到“企业架构”这个概念,转念一想必定和我们软件架构…

亚信安全发布《2024年第三季度网络安全威胁报告》

《亚信安全2024年第三季度网络安全威胁报告》的发布旨在从一个全面的视角解析当前的网络安全威胁环境。此报告通过详尽梳理和总结2024年第三季度的网络攻击威胁,目的是提供一个准确和直观的终端威胁感知。帮助用户更好地识别网络安全风险,并采取有效的防…

【c++】模板详解(2)

🌟🌟作者主页:ephemerals__ 🌟🌟所属专栏:C 目录 前言 一、非类型模板参数 二、模板的特化 1. 概念 2. 场景举例 3. 函数模板的特化 4. 类模板的特化 全特化 偏特化 1. 部分特化 2. 对参数的…

红队笔记--W1R3S、JARBAS、SickOS、Prime打靶练习记录

W1R3S(思路为主) 信息收集 首先使用nmap探测主机,得到192.168.190.147 接下来扫描端口,可以看到ports文件保存了三种格式 其中.nmap和屏幕输出的一样;xml这种的适合机器 nmap -sT --min-rate 10000 -p- 192.168.190.147 -oA nmapscan/ports…

Qt/C++基于重力模拟的像素点水平堆叠效果

本文将深入解析一个基于 Qt/C 的像素点模拟程序。程序通过 重力作用,将随机分布的像素点下落并水平堆叠,同时支持窗口动态拉伸后重新计算像素点分布。 程序功能概述 随机生成像素点:程序在初始化时随机生成一定数量的像素点,每个…

十一月二十五

双向循环链表 class Node:#显性定义出构造函数def __init__(self,data):self.data data #普通节点的数据域self.next None #保存下一个节点的链接域self.prior None #保存前一个节点饿链接域 class DoubleLinkLoop:def __init__(self, node Node):self.head nodeself.siz…

Python + 深度学习从 0 到 1(00 / 99)

希望对你有帮助呀!!💜💜 如有更好理解的思路,欢迎大家留言补充 ~ 一起加油叭 💦 欢迎关注、订阅专栏 【深度学习从 0 到 1】谢谢你的支持! ⭐ 什么是深度学习? 人工智能、机器学习与…

UG NX二次开发(C++)-UIStyler-指定平面的对象和参数获取

文章目录 1、前言2、在UG NX中创建平面和一个长方体,3、在UI Styler中创建一个UI界面4、在VS中创建一个工程4.1 创建并添加工程文件4.2 在Update_cb方法中添加选择平面的代码4.3 编译完成并测试效果1、前言 在采用NXOpen C++进行二次开发时,采用Menu/UIStyler是一种很常见的…

C# 命令行运行包

环境:net6 nuget包:Cliwrap 3.6.7 program: 相当于cmd运行命令:nuget search json static async Task Main(string[] args) {var cmd Cli.Wrap("D:\\软件\\Nuget\\nuget.exe").WithArguments(args >args.Add("…

长三角文博会:Adobe国际认证体系推动设计人才评价新标准

2024年11月22日,由上海、江苏、浙江、安徽三省一市党委宣传部共同发起的第五届长三角文化博览会(简称“长三角文博会”)在上海国家会展中心盛大启幕。长三角文博会自2018年起已成功举办多届,已成为展示区域文化产业发展成果、推动…

音视频基础扫盲之视频码率控制策略(CBR、VBR还是ABR)

视频码率控制策略 CBR(Constant Bit Rate)、VBR(Variable Bit Rate)和ABR(Average Bit Rate)是三种常见的比特率控制方式,以视频码率控制为例,视频码率控制策略主要是在保证视频质量…

【C语言】传值调用与传址调用:深度解析与实现

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C语言 文章目录 💯前言💯什么是传值调用和传址调用?1. 传值调用(Call by Value)2. 传址调用(Call by Reference) 💯传值调…

C++《二叉搜索树》

在初阶数据结构中我学习了树基础的概念以及了解了顺序结构的二叉树——堆和链式结构二叉树该如何实现,那么接下来我们将进一步的学习二叉树,在此会先后学习到二叉搜索树、AVL树、红黑树;通过这些的学习将让我们更易于理解后面set、map、哈希等…

Leetcode647. 回文子串(HOT100)

链接 代码&#xff1a; class Solution { public:int countSubstrings(string s) {int res 0;for(int i 0;i<s.size();i){for(int j i,k i;j>0&&k<s.size();j--,k){if(s[j]!s[k])break;else res;}for(int j i,k i1;j>0&&k<s.size();j--…

ubuntu, 安装部署comfyui,记录2:下载模型GGuf及测试

0.清除工作流 1.安装manager 2024年最新ComfyUI汉化及manager插件安装详解&#xff01;_comfyui-manager-CSDN博客 ComfyUI Manager安装 转到ComfyUI的安装目录ComfyUI/custom_nodes; 使用git拉取ComfyUI Manager&#xff0c;git clone https://github.com/ltdrdata/Comf…

【Y20030006】基于php+mysql的课程学习网站的设计与实现(附源码 配置 文档)

网络购物商城的设计与实现 1.摘要2.开发目的和意义3.系统功能设计4.系统界面截图5.源码获取 1.摘要 随着互联网的普及和在线教育的兴起&#xff0c;课程学习网站已经成为越来越多人获取知识和提升技能的重要途径。在这样的背景下&#xff0c;开发一个基于Laravel框架的课程学习…