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

前言

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

什么是前端设计模式

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

前端设计模式分类

详细来树洞额话设计模式高达二十多种,本文章主要针对于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.mzph.cn/pingmian/61417.shtml

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

相关文章

OpenCV从入门到精通实战(九)——基于dlib的疲劳监测 ear计算

本文实现Python库d和OpenCV来实现眼部闭合检测,主要用于评估用户是否眨眼。 步骤一:导入必要的库和设置参数 首先,代码导入了必要的Python库,如dlib、OpenCV和scipy。通过argparse设置了输入视频和面部标记预测器的参数。 from…

后端开发详细学习框架与路线

🚀 作者 :“码上有前” 🚀 文章简介 :后端开发 🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬 为帮助你合理安排时间,以下是结合上述学习内容的阶段划分与时间分配建议。时间安排灵活&a…

如何在 Ubuntu 上安装 Mosquitto MQTT 代理

如何在 Ubuntu 上安装 Mosquitto MQTT 代理 Mosquitto 是一个开源的消息代理,实现了消息队列遥测传输 (MQTT) 协议。在 Ubuntu 22.04 上安装 MQTT 代理,您可以利用 MQTT 轻量级的 TCP/IP 消息平台,该平台专为资源有限的物联网 (IoT) 设备设计…

Webserver回顾

线程池如何工作? 从请求队列中取出request请求,然后process处理 process是处理业务代码,用于解析http请求的 如何为线程上锁 由于线程共享同一块资源,为了避免线程重复读写资源的数据安全问题 发什么信号 定义信号 信号量如…

实验室资源调度系统:基于Spring Boot的创新

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…

STM32与CS创世SD NAND(贴片SD卡)结合完成FATFS文件系统移植与测试是一个涉及硬件与软件综合应用的复杂过程

一、前言 在STM32项目开发中,经常会用到存储芯片存储数据。 比如:关机时保存机器运行过程中的状态数据,上电再从存储芯片里读取数据恢复;在存储芯片里也会存放很多资源文件。比如,开机音乐,界面上的菜单图…

【在Linux世界中追寻伟大的One Piece】手写序列化与反序列化

目录 1 -> 序列化与反序列化概念 2 -> 序列化与反序列化作用和应用场景 3 -> 手写序列化与反序列化 1 -> 序列化与反序列化概念 序列化是指将对象的状态信息转换为可以存储或传输的形式的过程,通常涉及将数据结构或对象转换成字节流或字符串格式。反…

uniapp自动注册机制:easycom

传统 Vue 项目中,我们需要注册、导入组件之后才能使用组件。 uniapp 框架提供了一种组件自动注册机制,只要你在 components 文件夹下新建的组件满足 /components/组件名/组件名.vue 的命名规范,就能直接使用。 注意:组件的文件夹…

springboot基于微信小程序的停车场管理系统

摘 要 停车场管理系统是一种基于移动端的应用程序,旨在方便车主停车的事务办理。该小程序提供了便捷的停车和功能,使车主能够快速完成各项必要的手续和信息填写。旨在提供一种便捷、高效的预约停车方式,减少停车手续的时间和精力成本。通过该…

AI技术在电商行业的创新应用与未来发展

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

.NET9 - 新功能体验(一)

被微软形容为“迄今为止最高效、最现代、最安全、最智能、性能最高的.NET版本”——.NET 9已经发布有一周了,今天想和大家一起体验一下新功能。 此次.NET 9在性能、安全性和功能等方面进行了大量改进,包含了数千项的修改,今天主要和大家一起体…

【Oracle篇】SQL性能优化实战案例(从15秒优化到0.08秒)(第七篇,总共七篇)

💫《博主介绍》:✨又是一天没白过,我是奈斯,DBA一名✨ 💫《擅长领域》:✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux,也在扩展大数据方向的知识面✌️…

神经网络入门实战:(一)神经网络解决的两种问题,以及AI、机器学习、深度学习三者之间的逻辑关系

AI、机器学习、深度学习三者之间的逻辑关系: 两种问题 (1)回归问题 回归问题是指预测一个或多个连续值的任务。这些连续值可以是任意实数,比如价格、温度、分数等。 回归问题的目标通常是 找到一个函数 ,该函数可以…

深入解析TK技术下视频音频不同步的成因与解决方案

随着互联网和数字视频技术的飞速发展,音视频同步问题逐渐成为网络视频播放、直播、编辑等过程中不可忽视的技术难题。尤其是在采用TK(Transmission Keying)技术进行视频传输时,由于其特殊的时序同步要求,音视频不同步现…

豆包MarsCode算法题:最小周长巧克力板组合

问题描述 思路分析 这道题可以抽象为一个最优化问题: 问题分析 每个正方形的面积为 k ,对应的边长为 k ,周长为 4k 。给定整数 n ,我们需要找到若干正方形,使得它们的面积之和恰好等于 n: 同时尽量最小…

解析与修复vcruntime140_1.dll问题,总结四种vcruntime140_1.dll解决方法

在使用Windows系统的过程中,不少用户可能会遇到与vcruntime140_1.dll相关的问题。这个看似神秘的文件,其实在很多软件的运行中扮演着至关重要的角色。今天的这篇文章将教大家四种vcruntime140_1.dll解决方法。 一、vcruntime140_1.dll文件分析 &#xf…

WebGL进阶(九)光线

理论基础: 点光源 符合向量定义,末减初。 平行光 环境光 效果: 点光源 平行光 环境光 源码: 点光源 平行光 环境光 复盘:

【Amazon】亚马逊云科技Amazon DynamoDB 实践Amazon DynamoDB

Amazon DynamoDB 是一种完全托管的 NoSQL 数据库服务,专为高性能和可扩展性设计,特别适合需要快速响应和高吞吐量的应用场景,如移动应用、游戏、物联网和实时分析等。 工作原理 Amazon DynamoDB 在任何规模下响应时间一律达毫秒级&#xff…

【AIGC】ChatGPT提示词Prompt解析:拒绝的艺术:如何优雅地说“不“

引言 在人际交往的复杂网络中,学会优雅地拒绝是一种至关重要的社交智慧。很多人往往因为害怕伤害他人的感受,而选择敷衍、拖延或不置可否。 然而,真正的智慧在于如何用尊重和同理心传达"不"的信息。 本文将深入探讨优雅拒绝的艺术,帮助你在维护自身边界的同时,…

Java项目实战II基于微信小程序的农场驿站平台(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着移动互…