一篇文章理解前端的设计模式

news2024/11/22 14:25:23

前言

作为前端开发,如果是想要提升自己能力和技术水平,不能只是简单的重复造轮子,必须要深刻理解体会前端的设计模式,有助于自身能力的提升。

什么是前端设计模式

所谓前端的设计模式就是一种可以在多处地方重复使用的代码方案设计,只是不同的设计模式所应用的场景也有所不同。

前端设计模式分类

详细来树洞额话设计模式高达二十多种,本文章主要针对于javascript相关的设计模式,针对于其中的10种模式进行分类总结和介绍。 javascript的设计模式总共分为三大类型:创建型、结构性和行为型

  1. 创建型:单例模式、工厂模式、适配器模式等。

  2. 结构型:适配器模式、装饰器模式、代理模式等。

  3. 行为性:观察者模式、发布订阅模式、命令模式、模板模式等。

前端设计模式-创建型

创建型:用于创建的过程。通过确定规则对代码进行封装,减少创建过程中的重复代码,并且对创建制定规则提高规范和灵活性。

  1. 单例模式
    特点:确保一个类只有一个实例,并且提供一个访问它全局的访问点。
    优点:由于只有一个实例,所以全局唯一性,并且可以更好地控制共享资源优化性能。
    示例:

    const test = {
      name: '小明',
      age: '18',
    };
    ​
    export default test;
    ​
    import test from './test';
    ​
    console.log(test.name,test.age);  // 打印:小明,18
    上述例子定义test并且export default 暴露唯一的实例 test,符合确保一个类只有一个实例,并且提供了一个访问它的全局访问点原则。
  2. 工厂模式
    特点:对代码逻辑进行封装,只暴露出通用的接口直接调用。
    优点:对逻辑进行高度封装,降低耦合度,易于维护代码和提高后续扩展性。
    示例:

    // ------ 定义一个类 ------ 
    class testProduct {
      constructor(productName) {
        this.productName = productName;
      }
    ​
      getName() {
        console.log(`名称: ${this.productName}`);
      }
    }
    ​
    // ----- 定义一个工厂函数 -------
    function createProduct(name) {
      return new testProduct(name);
    }
    ​
    // 使用工厂函数创建对象
    const test1 = createProduct('test1');
    const test2 = createProduct('test2');
    ​
    // 使用对象
    test1.getName(); // 打印: 名称: test1
    test2.getName(); // 打印: 名称: test2
    ​上述例子定义一个工厂函数,逻辑代码封装在testProduct类中,暴露出createProduct方法,调用时传入不同的参数返回不同的内容。
  3. 构造器模式
    特点:定义一个通用的构造函数,方便多次传递参数调用。
    优点:减少代码重复量,提高可维护性和可拓展性。
    示例:

    class testPerson {
      constructor(name, age,) {
        this.name = name;
        this.age = age;
      }
    ​
      introduce() {
        console.log(`姓名: ${this.name}, 年龄: ${this.age}`);
      }
    }
    ​
    ​
    const test1 = new testPerson('张三', 18);
    test1.introduce(); //  姓名: 张三, 年龄: 18
    ​
    const test2 = new testPerson('李四', 20);
    test2.introduce(); // 输出: 姓名: 李四, 年龄: 20
    ​定义一个testPerson类,每次传入不同参数即可创建不同的用户对象,如果后续需要修改属性只需要调整testPerson类。

前端设计模式-结构型

结构型:主要针对对象之间的组合。通过增加代码的复杂度,从而提高扩展性和适配性,使代码兼容性更好、使某个方法功能更强大。

  1. 适配器模式
    特点:使某个类的接口有更强的适配性,比如本来支持mp3格式的,现在适配成能支持mp4格式的。
    优点:适配扩展后提高了复用性,降低耦合度并且增强了灵活性。
    示例:

    // ------ 本来存在需要被适配的mp3接口 ------
    class Receptacle {
      plugIn() {
        console.log("mp3");
      }
    }
    ​
    //  ------ 适配者类 ------ 
    class ForeignReceptacle {
      plugInMp4() {
        console.log("mp4");
      }
    }
    ​
    // ------ 用于适配的方法 ------
    class VoltageAdapter {
      constructor(foreignReceptacle) {
        this.foreignReceptacle = foreignReceptacle;
      }
      plugIn() {
        this.foreignReceptacle.plugInMp4();
      }
    }
    ​

    使用适配器代码:正常使用Receptacle类输出mp3,如果要适配mp4,那么使用定义的VoltageAdapter适配器把mp4的ForeignReceptacle类适配到Receptacle上。 这个方法扩展了Receptacle类的功能,也不需要修改Receptacle类。

    // 创建mp3
    const receptacle = new Receptacle();
    receptacle.plugIn(); // 打印输出: mp3
    ​
    // 创建mp4
    const foreignReceptacle = new ForeignReceptacle();
    ​
    // 使用适配器将 mp4 适配到 mp3
    const adapter = new VoltageAdapter(foreignReceptacle);
    adapter.plugIn(); // 打印输出: mp4
    ​

  2. 装饰器模式
    特点:创建一个对象去包裹原始对象,在不修改原始对象本身的情况下,动态的给指定对象添加新功能。
    优点:不改动原函数的情况下方便动态扩展功能,可以服用现有函数增强灵活性。
    示例:

    // 基础函数
    function getGreet(name) {
      console.log(`你好啊,${name}!`);
    }
    ​
    // 装饰器函数
    function welcomePrefix(greetFunction) {
      return function(name) {
        console.log("欢迎");
        greetFunction(name);
      };
    }
    ​
    // 基础函数
    getGreet("张三"); // 打印: 你好啊,张三!
    // 添加 欢迎啊 前缀
    const setWelcome = welcomePrefix(getGreet);
    setWelcome("张三"); // 打印: 欢迎     
                        // 打印: 你好啊,张三!
    ​getGreet只能输出你好啊**,但是使用装饰器函数welcomePrefix装饰后,可以在前面添加“欢迎啊”的前缀,通过这个实现思路方式不需要修改基础函数就能添加功能。
  3. 代理模式
    特点:给某个对象加一个代理对象,代理对象起到中介作用,中介对象在不改变原来对象情况下添加功能。
    优点:代理对象可以很方便实现拦截控制访问,并且不能修改原对象提高代码复用率。
    示例:

    // 基础函数
    function counterEvent() {
      let count = 0;
      return {
        setCount: () => {
          count += 1;
        },
        getCount: () => {
          return count;
        }
      };
    }
    ​
    // 代理函数
    function countProxy() {
      const newCounter = counterEvent();
      return {
        setCount: () => {
          newCounter.setCount();
        },
        getCount: () => {
          return newCounter.getCount();
        }
      };
    }
    ​
    // 创建一个代理对象
    const myCounter = countProxy();
    // 触发增加
    myCounter.setCount();
    myCounter.setCount();
    myCounter.setCount();
    // 获取当前数
    console.log(myCounter.getCount()); // 打印: 3
    ​代理模式不让用户直接操作原始函数counterEvent,而是通过代理函数countProxy去操作函数counterEvent。

前端设计模式-行为型

行为型:主要针对对象之间的交互。针对特定的应用场景,通过封装指定对象之间的交互方式规则,使对象之间协作更加灵活高效健壮。

  1. 观察者模式
    特点:观察某个对象是否发生变化,如果发生变化就会通知所有订阅者,并作出相应的操作,是一对一或一对多的关系。
    优点:有很强动态灵活性,可以很容易添加或移除观察者,把观察者和被观察者解耦进行逻辑分离易于维护。
    示例:

    // 观察者
    class Sub {
      constructor() {
        this.observers = [];
      }
    ​
      add(observer) { // 添加观察者到列表中
        this.observers.push(observer);
      }
    ​
      remove(observer) {  // 从列表中移除观察者
        this.observers = this.observers.filter(obs => obs !== observer);
      }
    ​
      notify(msg) {  // 通知所有观察者
        this.observers.forEach(observer => observer(msg));
      }
    }
    ​
    // 用于创建观察者
    const createObs = (name) => {
      return (msg) => {
        console.log(`${name} 收到: ${msg}`);
      };
    };
    被观察者Sub里面有add(添加)、remove(移除)和notify(通知)观察者的方法,观察者createObs里面有接收通知的方法。 使用sub.add 添加观察者后可以使用sub.notify发布消息通知所有观察者。 使用sub.remove移除观察者后不会再收到通知。
    // 创建一个被观察者
    const sub = new Sub();
    ​
    // 创建观察者
    const obs1 = createObs("观察者1");
    const obs2 = createObs("观察者2");
    ​
    // 订阅被观察者
    sub.add(obs1);
    sub.add(obs2);
    ​
    // 发布消息
    sub.notify("你好!"); // 观察者1和观察者2都收到: 你好!
                                  
    // 移除观察者1
    sub.remove(obs1);
    ​
    // 再次发布
    sub.notify("你好!"); // 只有观察者2收到: 你好!
  2. 发布订阅者模式
    特点:这个模式与观察者模式类似,但观察者模式是一对一或一对多,发布订阅者模式是多对多的关系,应用场景有所不同。
    优点:多对多关系有很强的动态灵活性,一个事件可以多个订阅者,一个订阅者可以订阅多个事件,把发布者和订阅者完全解耦提高灵活性和扩展性。
    示例:

    // 发布者
    class Pub {
      constructor() {
        this.subobj = {};
      }
    ​
      subscribe(event, callback) {  // 订阅事件
        if (!this.subobj[event]) {
          this.subobj[event] = [];
        }
        this.subobj[event].push(callback);
      }
    ​
      unsubscribe(event, callback) {  // 移除订阅事件
        if (this.subobj[event]) {
          this.subobj[event] = this.subobj[event].filter(cb => cb !== callback);
        }
      }
    ​
      publish(event, data) { // 发布事件
        if (this.subobj[event]) {
          this.subobj[event].forEach(callback => callback(data));
        }
      }
    }
    ​
    ​
    // 创建一个发布者实例
    const pub = new Pub();
    ​
    // 订阅者回调函数
    const subevent1 = (msg) => {
      console.log(`订阅者1 收到: ${msg}`);
    };
    ​
    const subevent2 = (msg) => {
      console.log(`订阅者2 收到: ${msg}`);
    };
    ​
    // 订阅事件
    pub.subscribe("greet", subevent1);
    pub.subscribe("greet", subevent2);
    ​
    // 发布消息
    pub.publish("greet", "你好!"); // 订阅者1和订阅者2 收到: 你好!
    ​
    // 移除一个订阅者
    pub.unsubscribe("greet", subevent1);
    ​
    // 再次发布消息
    pub.publish("greet", "你好!"); //  只有订阅者2 收到: 你好!
    ​

    定义一个Pub类,里面有subscribe(添加订阅事件)、unsubscribe(移除订阅事件)、publish(发布订阅事件)。 new Pub() 创建一个发布者示例,可以添加、移除和发布事件。 this.subobj = {} 存放事件映射,而不是数组,{}里面的每个事件都可以存放一个订阅者数组从而实现多对多的关系。

  3. 命令模式
    特点:把请求封装在对象里面整个传递给调用对象,使里面参数更加灵活方便易于扩展。
    优点:使发送和接收者完全解耦独立易于数据维护、逻辑独立方便灵活处理、队列请求可以撤销操作。
    示例:

    // 接收者
    class testLight {
      on() {
        console.log("打开灯了");
      }
      off() {
        console.log("关闭灯了");
      }
    }
    ​
    // 命令基类
    class Comm {
      constructor(receiver) {
        this.receiver = receiver;
      }
    }
    ​
    // 具体命令
    class LightOnComm extends Comm {
      execute() {
        this.receiver.on();
      }
    }
    ​
    class LightOffComm extends Comm {
      execute() {
        this.receiver.off();
      }
    }
    ​
    // 调用者
    class RemoteControl {
      onButton(comm) {
        comm.execute();
      }
    }
    ​
    ​

    接收者testLight主要负责执行业务逻辑命令,即决定是否关灯。 LightOnComm和LightOffComm继承Comm类,实现execute()方法,在其中分布调用on和off方法。 RemoteControl类负责调用者的方法,即去调用execute()方法。

    // 使用
    const testlight = new testLight();
    const lightOnComm = new LightOnComm(testlight);
    const lightOffComm = new LightOffComm(testlight);
    const remoteControl = new RemoteControl();
    ​
    remoteControl.onButton(lightOnComm); // 输出: 打开灯了
    remoteControl.onButton(lightOffComm); // 输出: 关闭灯了
  4. 模板模式
    特点:定义好整个操作过程的框架,框架中把每个步骤的逻辑独立处理。
    优点:步骤独立分开管理,易于扩展功能维护代码。
    示例:

    class Game {
      constructor(obj) {
         
      }
      initGame() {
          console.log('初始化');
      }
      startGame() {
        console.log('游戏开始');
      }
      onGame() {
        console.log('游戏中');
      }
      endGame() {
        console.log('游戏结束');
      }
      personEntry() {
          this.initGame()
          this.startGame()
          this.onGame()
          this.endGame()
      }
    }
    ​

    这个Game类中把每个步骤的逻辑都放在对应步骤方法中,独立管理互不影响。添加或者减少步骤,只需要修改对应的方法即可。

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

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

相关文章

Linux驱动编程 - kmalloc、vmalloc区别

目录 前言: 1、区别 2、使用差异 一、kmalloc、kzalloc、kfree 1、动态申请 1.1 kmalloc() 1.2 kzalloc() 2、内存释放 3、示例 二、vmalloc、vzalloc、vfree 1、动态申请 1.1 vmalloc() 1.2 vzalloc() 2、内存释放 3、示例 前言: Linux内…

使用低成本的蓝牙HID硬件模拟鼠标和键盘来实现自动化脚本

做过自动化脚本的都知道,现在很多传统的自动化脚本方案几乎都可以被检测,比如基于root,adb等方案。用外置的带有鼠标和键盘功能集的蓝牙HID硬件来直接点击和滑动是非常靠谱的方案,也是未来的趋势所在。 一、使用蓝牙HID硬件的优势…

VideoCrafter模型部署教程

一、介绍 VideoCrafter是一个功能强大的AI视频编辑和生成工具,它结合了深度学习和机器学习技术,为用户提供了便捷的视频制作和编辑体验。 系统:Ubuntu22.04系统,显卡:4090,显存:24G 二、基础…

#渗透测试#SRC漏洞挖掘#Python自动化脚本的编写05之多线程与多进程

免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停…

C++多继承:一个子类继承多个父类的情况

C的类继承大家还算比较了解。它主要包括单继承、多继承、虚继承这几方面。 单继承就是一个子类只继承一个父类,多继承就是一个子类继承多个父类。 其实在C中,一个子类继承多个父类的情况还是比较常见的。比如,一个子类需要同时继承两个父类…

在windows电脑上安装docker服务

以下是在 Windows 电脑上安装 Docker 服务的详细步骤: 一、下载 Docker Desktop for Windows 系统要求:Windows 操作系统需要是 Windows 10(64 位)专业版、企业版或教育版,或者是 Windows 11。并且系统要开启了硬件虚…

单片机UART协议相关知识

概念 UART(Universal Asynchronous Receiver/Transmitter,通用异步收发传输器) 是一种 异步 串行 全双工 通信协议,用于设备一对一进行数据传输,只需要两根线(TX,RX)。 异步&…

XXL-JOB执行任务的SpringBoot程序无法注册到调度中心

文章目录 1. 问题呈现2. 问题产生的原因2.1 原因一:执行器和调度中心部署在不同的机器上2.2 原因二:调度中心部署在云服务器上 3. 解决方法3.1 方法一:将执行器和调度中心部署在同一台机器上3.2 方法二:手动指定执行器的ip地址&am…

Ettus USRP X410

总线连接器: 以太网 RF频率范围: 1 MHz 至 7.2 GHz GPSDO: 是 输出通道数量: 4 RF收发仪瞬时带宽: 400 MHz 输入通道数量: 4 FPGA: Zynq US RFSoC (ZU28DR) 1 MHz to 7.2 GHz,400 MHz带宽,GPS驯服OCXO,USRP软件无线电设备 Ettus USRP X410集…

哋它亢SEO技术分析:如何提升网站在搜索引擎中的可见性

文章目录 哋它亢SEO技术分析:如何提升网站在搜索引擎中的可见性网站的基本情况SEO优化分析与建议1. 元数据优化2. 关键词优化3. URL结构4. 图像优化5. 移动端优化6. 网站速度7. 结构化数据(Schema Markup)8. 内链与外链9. 社交分享 哋它亢SEO…

将网站地址改成https地址需要哪些材料

HTTPS(安全超文本传输协议)是HTTP协议的扩展。它大大降低了个人数据(用户名、密码、银行卡号等)被拦截的风险,还有助于防止加载网站时的内容替换,包括广告替换。 在发送数据之前,信息会使用SSL…

mongodb多表查询,五个表查询

需求是这样的,而数据是从mysql导入进来的,由于mysql不支持数组类型的数据,所以有很多关联表。药剂里找药物,需要药剂与药物的关联表,然后再找药物表。从药物表里再找药物与成分关联表,最后再找成分表。 这里…

端到端的专线管理与运维:实时掌握专线的运行状态

在当今高度信息化的时代,专线服务已成为企业数据传输的重要组成部分。为了确保专线服务的高效、稳定运行,我们采用了先进的端到端管理模式,对专线的运行状态和质量进行全面监控。本文将从专线管理的必要性、端到端管理模式的优势、实施步骤以…

SpringBoot(8)-任务

目录 一、异步任务 二、定时任务 三、邮件任务 一、异步任务 使用场景:后端发送邮件需要时间,前端若响应不动会导致体验感不佳,一般会采用多线程的方式去处理这些任务,但每次都需要自己去手动编写多线程来实现 1、编写servic…

PostgreSQL常用字符串函数与示例说明

文章目录 coalesce字符串位置(position strpos)字符串长度与大小写转换去掉空格(trim ltrim rtrim)字符串连接(concat)字符串替换简单替换(replace)替换指定位置长度(overlay)正则替换(regexp_replace) 字符串匹配字符串拆分split_part(拆分数组取指定位置的值)string_to_array…

深入剖析Java内存管理:机制、优化与最佳实践

🚀 作者 :“码上有前” 🚀 文章简介 :Java 🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬 深入剖析Java内存管理:机制、优化与最佳实践 一、Java内存模型概述 1. Java内存模型的定义与作…

【论文速读】| RobustKV:通过键值对驱逐防御大语言模型免受越狱攻击

基本信息 原文标题:ROBUSTKV: DEFENDING LARGE LANGUAGE MODELS AGAINST JAILBREAK ATTACKS VIA KV EVICTION 原文作者:Tanqiu Jiang, Zian Wang, Jiacheng Liang, Changjiang Li, Yuhui Wang, Ting Wang 作者单位:Stony Brook University…

css使用弹性盒,让每个子元素平均等分父元素的4/1大小

css使用弹性盒,让每个子元素平均等分父元素的4/1大小 原本: ul {padding: 0;width: 100%;background-color: rgb(74, 80, 62);display: flex;justify-content: space-between;flex-wrap: wrap;li {/* 每个占4/1 */overflow: hidden;background-color: r…

图像处理 之 凸包和最小外围轮廓生成

“ 最小包围轮廓之美” 一起来欣赏图形之美~ 1.原始图片 男人牵着机器狗 2.轮廓提取 轮廓提取 3.最小包围轮廓 最小包围轮廓 4.凸包 凸包 5.凸包和最小包围轮廓的合照 凸包和最小包围轮廓的合照 上述图片中凸包、最小外围轮廓效果为作者实现算法生成。 图形几何之美系列&#…

徒手从零搭建一套ELK日志平台

徒手从零搭建一套ELK日志平台 日志分析的概述日志分析的作用主要收集工具集中式日志系统主要特点采集日志分类ELK概述初级版ELK终极版ELK高级版ELKELK收集日志的两种形式 搭建ELK平台Logstash工作原理Logstash核心概念环境准备安装部署docker添加镜像加速器安装部署Elasticsear…