前端常用的设计模式
设计模式:是一种抽象的编程思想,并不局限于某一特定的编程语言,而是在许多语言之间是相通的;它是软件设计中常见的问题的通用、可反复使用、多少人知晓的一种解决方案或者模板。一般对与从事过面向对象编程的人来说会更熟悉一些。
设计模式的意义:指导我们如何写出可维护、可复用、可扩展及灵活的代码。
接下来我们来简单了解一下前端开发过程中的一些常用的设计模式。
1.单例模式
使用场景
当需要一个对象去贯穿整个应用系统中,且这个对象中的数据是系统中全局共享的。比如,Vue 中的 Vuex 中的 store。
实现方式
通过隐藏指定的Class 构造函数来创建或获取唯一实例的一种实现方式;
使用闭包保存局部作用域中的单例对象并返回。
const SingleInstance = (function(){function SingleClas(){}let singleObj;return {getInstance:function(){if(!singleObj){singleObj = new SingleClas();}return singleObj;}}
})();const instance1 = SingleInstance.getInstance();
const instance2 = SingleInstance.getInstance();
console.log(instance1 === instance2);//true
注意事项
因为单例模式会引入全局状态,所以应用开发过程中尽量避免大量的单例模式的使用。
2. 发布-订阅模式(有的叫消息队列模式)
思想:
在发布-订阅模式中,有两种类型的对象:
- 发布者: 是事件的发出者,通常维护一个事件列表,并且可以向列表中添加或者删除事件;当某个事件发生时,将这个事件通知给所有的订阅者。
- 订阅者:是事件的接收者,它们订阅了某个事件,并且在这个事件发生时接收对应的通知。
使用场景
他是一种将发布者和订阅者解耦的设计模式,在前端开发中,可以使用该模式实现组件之间的通信。这个模式应该是我们平时接触的最多的了,JavaScript 中的事件订阅响应机制。比如 Vue 中的 事件总线
实现方式
//定义一个发布者
let publisher = {let events:{},//定义一个对象,用来存放事件列表//定义添加事件的方法,添加到事件列表中addEvent(event,callback){if(!events[event]){this.events[event] =[];}this.events[event].push(callback);},//定义删除事件的方法removeEvent(event,callback){if(this.events[event]){for(let i = 0;i<this.events[event].length; i++){if(this.events[event][i] === callback){this.events[event].splice(i,1);break;}}}},//发布事件publishEvent(event,data){if(this.events[event]){for(let i=0;i < this.events[event].length; i ++){this.events[event][i](data);}}}
};//定义一个订阅者
let subscriber1 = {//处理事件回调handleEvent(data){//...这里处理data操作}
};//订阅事件
publisher.addEvent('event1',subscriber1.handleEvent);//发布事件
publisher.publishEvent('event','Hi event1 happend');//取消订阅
publisher.removeEvent('event1',subscriber1.handleEvent);
3. 观察者模式 ‘等效于’ 发布-订阅模式
使用场景
该模式跟发布订阅模式有点像,有很多地方把这个模式等同于发布-订阅模式。
当对象间存在一对多的依赖关系时,可以使用观察者模式,当被观察的对象发生变化时,其所有的观察者都会收到通知并进行相应的操作。在 javascript 中可以使用回调函数或者事件监听来实现观察者模式。观察者模式通常被用来实现组件间的数据传递和事件处理。比如 React 中的 Redux 和事件处理库 EventEmitter
实现方式
- 定一个被观察对象 Subject,当其发生改变时通知所有的观察者;
- 定义观察者 Observer ,是观察被观察对象的对象,当Subject 发生改变时,会接收到通知并进行相应的处理。
class Subject{constructor(){ this.observers =[];}addObserver(observer){this.observers.push(observer);}removeObserver(observer){this.observers = this.observer.filter(ob => ob !== observer);}notify(data){this.observers.forEach(ob => ob.update(data));}
}class Observer{update(data){//...这里处理回调逻辑console.log(data+'data发生改变了');}
}const subject = new Subject();
const ob1 = new Observer();
const ob2 = new Observer();subject.addObserver(ob1);
subject.addObserver(ob2);
subject.notify('wow');
//wow data发生改变了
//wow data发生改变了
4. 装饰者模式
使用场景
动态的给一个对象或者组件添加额外的行为或样式。可以让我在不改变原有代码的情况下给组件添加新的行为和样式。这也是‘装饰’一词的由来,不改变原有的,继续往上添加行为或样式。
实现方式
方式一:通过扩展对象的属性或者方法实现
//原始对象
const obj = {f1(){console.log("我是 f1");}
}
//创建一个装饰函数,扩展 obj 的方法
const decoratorFn=(obj)=>{obj.f2 = ()=>{console.log("我是 f2");}return obj;
}const decObj = decoratorFn(obj);//装饰 obj
decObj.f1();//我是 f1
decObj.f2();//我是 f2
方式二:通过扩展对象的原型来实现
//定义一个原始对象
function F1(){}
//在对象的原向上定义一个方法
F1.prototype.f1 = ()=>{console.log("我是 f1");}
//定义一个装饰函数
const decoratorFn=(clazz)=>{clazz.prototype.f2= ()=>{console.log("我是 f2");}
}
//使用装饰器函数扩展原型
decoratorFn(F1);
const obj = new F1();
obj.f1();//我是 f1
obj.f2();//我是 f2
5. 代理模式
思想:
为一个对象提供一个替代品或者是占位符,以便控制对它的访问。在前端开发中该模式经常被用来处理一些复杂或者耗时的操作,它通过引入一个代理对象来控制对原是对象的访问,这个代理对象类似于原始对象的中介,客户跟中介交互,中介再跟原始对象交互。有的地方也叫 “中介者模式”。
比如:图片的懒加载、缓存等。
实现方式
//我们第一个原始的 image 对象
class Image{constructor(url){this.url = url;}//定义加载图片的方法load(){console.log('加载图片')}
}//定义一个代理对象 实现延迟加载图片
class ProxyImage{constructor(url){this.url = url;this.image = null;}//定义加载图片的方法load(){if(!this.image){this.image = new Image(this.url)console.log('延迟加载');//可以使用占位符代替}this.image.load();//加载图片}
}const img1 = new ProxyImage('http://...img1.png');
const img2 = new ProxyImage('http://...img2.png');
img1.load();//延迟加载, 加载图片
img1.load();//加载图片
img2.load();//延迟加载 加载图片
上述的实现方式,如果图片已经被加载过了,代理对象就会直接显示图片,否则代理对象会加载占位符,并延迟加载,如果已经加载过了,代理对象就直接显示图片。通过这种模式就可以在不影响原始对象的情况下,实现图片的懒加载。
除了以上的模式,常见的还有 【工厂模式】、【迭代器模式】、【策略模式】等,由于篇幅有限,后续再补上。
相关文章:
前端常用的设计模式
设计模式:是一种抽象的编程思想,并不局限于某一特定的编程语言,而是在许多语言之间是相通的;它是软件设计中常见的问题的通用、可反复使用、多少人知晓的一种解决方案或者模板。一般对与从事过面向对象编程的人来说会更熟悉一些。…...

游戏引擎支持脚本编程有啥好处
很多游戏引擎都支持脚本编程。Unity、Unreal Engine、CryEngine等大型游戏引擎都支持使用脚本编写游戏逻辑和功能。脚本编程通常使用C#、Lua或Python等编程语言,并且可以与游戏引擎的API进行交互来控制游戏对象、设置变量、执行行为等。使用脚本编程,游戏…...
react中概念性总结(二)
目录 说说你对react的理解?有哪些特性? 说说Real diff算法是怎么运作的,从tree层到component层到element层分别讲解? 调和阶段setState干了什么? 说说redux的工作流程? 为什么react元素有一个$$type属…...

WPF自定义漂亮顶部工具栏 WPF自定义精致最大化关闭工具栏 wpf导航栏自定义 WPF快速开发工具栏
在WPF应用程序开发中,自定义一个漂亮的顶部工具栏具有多重关键作用,它不仅增强了用户体验,还提升了整体应用的专业性和易用性。以下是对这一功能的详细介绍: 首先,自定义顶部工具栏是用户界面设计的重要组成部分&…...

Transformer 的双向编码器表示 (BERT)
一、说明 本文介绍语言句法中,最可能的单词填空在self-attention的表现形式,以及内部原理的介绍。 二、关于本文概述 在我之前的博客中,我们研究了关于生成式预训练 Transformer 的完整概述,关于生成式预训练 Transformer (GPT) 的…...

关于LwRB环形缓冲区开源库的纯C++版本支持原子操作
1、LwRB环形缓冲区开源库: GitHub - MaJerle/lwrb: Lightweight generic ring buffer manager libraryLightweight generic ring buffer manager library. Contribute to MaJerle/lwrb development by creating an account on GitHub.https://github.com/MaJerle/l…...
微信小程序Canvas画布绘制图片、文字、矩形、(椭)圆、直线
获取CanvasRenderingContext2D 对象 .js onReady() {const query = wx.createSelectorQuery()query.select(#myCanvas).fields({ node: true, size: true }).exec((res) => {const canvas = res[0].nodeconst ctx = canvas.getContext(2d)canvas.width = res[0].width * d…...

Unity Editor实用功能:Hierarchy面板的对象上绘制按按钮并响应
目录 需求描述上代码打个赏吧 需求描述 现在有这样一个需求: 在Hierarchy面板的对象上绘制按钮点击按钮,弹出菜单再点击菜单项目响应自定义操作在这里的响应主要是复制对象层级路路径 看具体效果请看动图: 注: 核心是对Edito…...

解决录制的 mp4 视频文件在 windows 无法播放的问题
解决录制的 mp4 视频文件在 windows 无法播放的问题 kazam 默认录制保存下来的 mp4 视频文件在 windows 中是无法直接使用的,这是由于视频编码方式的问题。解决办法: 首先安装 ffmeg 编码工具: sudo apt-get install ffmpeg 然后改变视频的…...

一键与图片对话!LLM实现图片关键信息提取与交互
本期文心开发者说邀请到飞桨开发者技术专家徐嘉祁,主要介绍了如何通过小模型与大模型的结合,解决数据分析中的问题。 项目背景 在智能涌现的大模型时代,越来越多的企业和研究机构开始探索如何利用大模型来提升工作效率,助力业务智…...
洛谷 P8833 [传智杯 #3 决赛] 课程 讲解
前言: 大家好! 我们又见面啦~~~ 对于我20多天没上号,深表歉意!! 希望大家给我的account点一个赞,加一个粉丝,谢谢! 也对CSDN的所有博主们送上衷心的祝福! 如有错误…...

中国IT产经新闻:新能源汽车发展前景与燃油车的利弊之争
随着科技的进步和环保意识的提高,新能源汽车在全球范围内逐渐受到重视。然而,在新能源汽车迅速发展的同时,燃油车仍然占据着主导地位。本文将从新能源与燃油车的利弊、新能源汽车的发展前景两个方面进行分析,以期为读者提供全面的…...
一、数据结构
一、 数组 1.1 数组 定义 遍历 // 遍历数组 传递指针 func traverse() {var b [...]int{1, 2, 3} //长度为3 元素为 1 2 3var ptr &b //ptr是指向数组的指针fmt.Println(b[0], b[1]) // 打印数组的前 2 个元素fmt.Println(ptr[0], ptr[1]) // 通…...
案例分享:各行业销售岗位的KPI指标制定分享
在当今竞争激烈的市场环境中,销售岗位的绩效考核至关重要。有效的绩效考核能帮助企业了解销售人员的业绩,激励他们提高效率,并确保销售战略的实现。关键绩效指标(KPI)作为绩效考核的核心,能精炼地反映销售人…...

【办公类-19-01】20240108图书统计登记表制作(23个班级)EXCEL复制表格并合并表格
背景需求: 制作一个EXCEL模板,每个班级的班主任统计 班级图书量(一个孩子10本,最多35个孩子350本) EXCEL模板 1.0版本: 将这个模板制作N份——每班一份 项目:班级图书统计表 核心:一个EXCEL模板批量生成…...

spring boot 2升级为spring boot 3中数据库连接池druid的问题
目录 ConfigurationClassPostProcessor ConfigurationClassBeanDefinitionReader MybatisPlusAutoConfiguration ConditionEvaluator OnBeanCondition 总结 近期给了一个任务,要求是对现有的 spring boot 2.x 项目进行升级,由于 spring boot 2.x 版…...
客服系统配置之Nginx处理静态资源和动态请求
Nginx直接处理静态资源,接口动态请求走反向代理到后端 这样可以减轻后端服务的压力 location / {try_files $uri kefu; }location kefu {# 这里是命名位置 kefu 的配置proxy_pass http://backend-server;# 其他反向代理的配置... }如果请求的是静态资源(…...
Golang 切片
前言 在Go语言中,切片是一个引用类型,它提供了对数组的动态窗口。切片并不存储任何数据,它只是描述了底层数组中的一个片段。切片的定义包括三个部分:指向数组的指针、切片的长度和切片的容量 基本使用 声明切片:声…...

防止公司办公终端文件数据 | 资料外泄,——自动智能透明加密防泄密软件系统
天锐绿盾公司电脑文件数据资料透明加密防泄密软件系统是一款专门用于保护企业电脑文件数据安全的软件系统。它采用透明加密技术,能够在不影响员工正常工作的情况下,对电脑上的文件数据进行自动加密,从而有效防止企业数据泄密。 PC端访问地址&…...
C#-枚举
枚举类型 (enum type) 是具有一组命名常量的独特的值类型。 下面的示例声明并使用一个名为 Color 的枚举类型,该枚举具有三个常量值 Red、Green 和 Blue: using System; using System;enum Color {Red,Green,Blue }class Test {static void PrintColor(…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...