前端常见七大设计模式

目录

前言

一、常见七大设计者模式

1.工厂模式

2.单例模式

3.适配器模式

4.装饰模式

5.代理模式

6.发布-订阅模式

7.外观模式

二、总结


前言

在前端开发中,设计模式总的来说是一个抽象的概念,前人通过无数次的实践总结出的一套写代码的方式,通过这种方式写的代码可以让别人更加容易阅读、维护以及复用。本文将会介绍前端常见的设计模式,并通过代码详解它们的实现。

一、常见七大设计者模式

1.工厂模式

 工厂模式分为好几种,这里就不一一讲解了,以下是一个简单工厂模式的例子。

class Man {constructor(name) {this.name = name}alertName() {alert(this.name)}
}class Factory {static create(name) {return new Man(name)}
}Factory.create('yck').alertName()

当然工厂模式并不仅仅是用来 new 出实例

可以想象一个场景。假设有一份很复杂的代码需要用户去调用,但是用户并不关心这些复杂的代码,只需要你提供给我一个接口去调用,用户只负责传递需要的参数,至于这些参数怎么使用,内部有什么逻辑是不关心的,只需要你最后返回我一个实例。这个构造过程就是工厂。

工厂起到的作用就是隐藏了创建实例的复杂度,只需要提供一个接口,简单清晰。

在 Vue 源码中,你也可以看到工厂模式的使用,比如创建异步组件。

export function createComponent (Ctor: Class<Component> | Function | Object | void,data: ?VNodeData,context: Component,children: ?Array<VNode>,tag?: string
): VNode | Array<VNode> | void {// 逻辑处理...const vnode = new VNode(`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,data, undefined, undefined, undefined, context,{ Ctor, propsData, listeners, tag, children },asyncFactory)return vnode
}

在上述代码中,我们可以看到我们只需要调用 createComponent 传入参数就能创建一个组件实例,但是创建这个实例是很复杂的一个过程,工厂帮助我们隐藏了这个复杂的过程,只需要一句代码调用就能实现功能。

2.单例模式

单例模式很常用,比如全局缓存、全局状态管理等等这些只需要一个对象,就可以使用单例模式。单例模式的核心就是保证全局只有一个对象可以访问。因为 JS 是门无类的语言,所以别的语言实现单例的方式并不能套入 JS 中,我们只需要用一个变量确保实例只创建一次就行,以下是如何实现单例模式的例子。

class Singleton {constructor() {}
}Singleton.getInstance = (function() {let instancereturn function() {if (!instance) {instance = new Singleton()}return instance}
})()let s1 = Singleton.getInstance()
let s2 = Singleton.getInstance()
console.log(s1 === s2) // true

在 Vuex 源码中,你也可以看到单例模式的使用,虽然它的实现方式不大一样,通过一个外部变量来控制只安装一次 Vuex。

let Vue // bind on installexport function install (_Vue) {if (Vue && _Vue === Vue) {// 如果发现 Vue 有值,就不重新创建实例了return}Vue = _VueapplyMixin(Vue)
}

3.适配器模式

适配器用来解决两个接口不兼容的情况,不需要改变已有的接口,通过包装一层的方式实现两个接口的正常协作。

以下是如何实现适配器模式的例子。

class Plug {getName() {return '港版插头'}
}class Target {constructor() {this.plug = new Plug()}getName() {return this.plug.getName() + ' 适配器转二脚插头'}
}let target = new Target()
target.getName() // 港版插头 适配器转二脚插头

在 Vue 中,我们其实经常使用到适配器模式。比如父组件传递给子组件一个时间戳属性,组件内部需要将时间戳转为正常的日期显示,一般会使用 computed 来做转换这件事情,这个过程就使用到了适配器模式。

4.装饰模式

装饰模式不需要改变已有的接口,作用是给对象添加功能。就像我们经常需要给手机戴个保护套防摔一样,不改变手机自身,给手机添加了保护套提供防摔功能。

以下是如何实现装饰模式的例子,使用了 ES7 中的装饰器语法。

function readonly(target, key, descriptor) {descriptor.writable = falsereturn descriptor
}class Test {@readonlyname = 'yck'
}let t = new Test()t.yck = '111' // 不可修改

在 React 中,装饰模式其实随处可见。

import { connect } from 'react-redux'
class MyComponent extends React.Component {// ...
}
export default connect(mapStateToProps)(MyComponent)

5.代理模式

代理是为了控制对对象的访问,不让外部直接访问到对象。在现实生活中,也有很多代理的场景。比如你需要买一件国外的产品,这时候你可以通过代购来购买产品。

在实际代码中其实代理的场景很多,也就不举框架中的例子了,比如事件代理就用到了代理模式。

<ul id="ul"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li>
</ul>
<script>let ul = document.querySelector('#ul')ul.addEventListener('click', (event) => {console.log(event.target);})
</script>

因为存在太多的 li,不可能每个都去绑定事件。这时候可以通过给父节点绑定一个事件,让父节点作为代理去拿到真实点击的节点。

6.发布-订阅模式

发布-订阅模式也叫做观察者模式。通过一对一或者一对多的依赖关系,当对象发生改变时,订阅方都会收到通知。在现实生活中,也有很多类似场景,比如我需要在购物网站上购买一个产品,但是发现该产品目前处于缺货状态,这时候我可以点击有货通知的按钮,让网站在产品有货的时候通过短信通知我。

在实际代码中其实发布-订阅模式也很常见,比如我们点击一个按钮触发了点击事件就是使用了该模式。

<ul id="ul"></ul>
<script>let ul = document.querySelector('#ul')ul.addEventListener('click', (event) => {console.log(event.target);})
</script>

在 Vue 中,如何实现响应式也是使用了该模式。对于需要实现响应式的对象来说,在 get 的时候会进行依赖收集,当改变了对象的属性时,就会触发派发更新。

7.外观模式

外观模式提供了一个接口,隐藏了内部的逻辑,更加方便外部调用。

举个例子来说,我们现在需要实现一个兼容多种浏览器的添加事件方法。

function addEvent(elm, evType, fn, useCapture) {if (elm.addEventListener) {elm.addEventListener(evType, fn, useCapture)return true} else if (elm.attachEvent) {var r = elm.attachEvent("on" + evType, fn)return r} else {elm["on" + evType] = fn}
}

对于不同的浏览器,添加事件的方式可能会存在兼容问题。如果每次都需要去这样写一遍的话肯定是不能接受的,所以我们将这些判断逻辑统一封装在一个接口中,外部需要添加事件只需要调用即可。

二、总结

以上就是七种常见的设计模式以及它们的应用场景和示例代码。当然,这只是冰山一角,还有很多其他的设计模式可以应用于不同的场景。熟悉各种设计模式并且能够灵活运用它们可以让你成为更优秀的开发者。 

如果文章对你有所帮助,❤️关注+点赞❤️鼓励一下!博主会持续更新。。。。

我的博客原文:https://code-nav.top/article/343

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

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

相关文章

创建第一个electron项目

安装 创建package.json npm init创建electron npm install --save-dev electron创建程序主入口文件 main.js,electron分为主进程和渲染进程,main,js也可以理解为electron项目的主进程,在主进程中是node环境不可以写操作dom方法,在渲染进程中是浏览器环境 可以写操作dom的一些…

12.28_黑马数据结构与算法笔记Java

目录 291 最长公共子序列 动态规划 分析 292 最长公共子序列 动态规划 实现 293 Leetcode 583 两个字符串删除 294 Leetcode 300 最长递增子序列 分析 295 Leetcode 300 最长递增子序列 实现 296 Catalan数 分析 297 Catalan数 实现 298 Catalan数 应用 出栈总数 299 C…

【C++】vector 基本使用(详解)

目录 一&#xff0c;vector 的介绍 二&#xff0c;vector 的定义 1&#xff0c;vector() 2&#xff0c;vector&#xff08;size_type n, const value_type& val value_type()&#xff09; 3&#xff0c;vector (const vector& x) 4&#xff0c;vector (InputIte…

PAT乙级 1025 反转链表

给定一个常数 K 以及一个单链表 L&#xff0c;请编写程序将 L 中每 K 个结点反转。例如&#xff1a;给定 L 为 1→2→3→4→5→6&#xff0c;K 为 3&#xff0c;则输出应该为 3→2→1→6→5→4&#xff1b;如果 K 为 4&#xff0c;则输出应该为 4→3→2→1→5→6&#xff0c;即…

【Redis前奏曲】初识Redis

文章目录 一.Redis的一些特性(优点)1. 在内存中存储数据2. 可编程的3. 可扩展的4.持久化5. 聚集(集群)6. 高可用Redis快的原因 二. 使用案例1.数据库2. 缓存3. 消息队列 一.Redis的一些特性(优点) 我们在上一篇博客中说到,Redis是一个在内存中存储数据的中间件.用作数据库,数据…

本地部署Python Flask并搭建web问答应用程序框架实现远程访问

文章目录 前言1. 安装部署Flask并制作SayHello问答界面2. 安装Cpolar内网穿透3. 配置Flask的问答界面公网访问地址4. 公网远程访问Flask的问答界面 前言 Flask是一个Python编写的Web微框架&#xff0c;让我们可以使用Python语言快速实现一个网站或Web服务&#xff0c;本期教程…

比起 Pandas, 你更需要 Polars:详细指南

在数据分析领域&#xff0c;Python 由于其多功能性和广泛的库生态系统而成为一种流行的语言。数据处理和分析在提取见解和做出明智决策方面发挥着至关重要的作用。然而&#xff0c;随着数据集的规模和复杂性不断增长&#xff0c;对高性能解决方案的需求变得至关重要。 有效地处…

bat 脚本 启动、停止、重启 SpringBoot 服务命令

启动 echo off chcp 65001 echo. title XXXX微服务项目后台程序 echo 启动SpringBoot服务 echo.:: 参数赋值 set JAVA_OPTS-Xms512m -Xmx1024m -XX:MetaspaceSize128m -XX:MaxMetaspaceSize512m :: 启动服务命令 java -jar -Dfile.encodingutf-8 %JAVA_OPTS% demo1.jar:: 因为…

【Docker】添加指定用户到指定用户组

运行Docker ps命令&#xff0c;报错&#xff1a;/v1.24/containers/json": dial unix /var/run/docker.sock: connect: permission denied 创建docker用户组 安装docker时默认已经创建好 sudo groupadd docker添加用户加入docker用户组 此处以用户user为例 sudo usermo…

flutter学习-day22-使用GestureDetector识别手势事件

文章目录 1. 介绍2. 使用2-1. 单击双击和长按2-2. 拖动和滑动2-3. 缩放 3. 注意点 1. 介绍 在 flutter 中&#xff0c;GestureDetector 是手势识别的组件&#xff0c;可以识别点击、双击、长按、拖动、缩放等手势事件&#xff0c;并且可以与子组件进行交互&#xff0c;构造函数…

cf918div4的E题讲解

虽然是div4&#xff0c;但是打的稀烂&#xff0c;哭死。 E题看了jiangly的题解豁然开朗&#xff0c;原来思路这么简单。被自己蠢到了&#xff0c;我傻傻的用前缀和&#xff0c;两层for枚举区间&#xff0c;不出意外TLE了&#xff0c;写的那一刻就知道要超时&#xff0c;但是没…

微信小程序全部内嵌H5遇到的问题记录

微信小程序内嵌H5遇到的问题 问题1&#xff1a;js-sdk的配置h5里面微信公众平台 问题2&#xff1a;业务域名的配置小程序开发微信公众平台 问题3&#xff1a;H5与小程序的消息传递。方案1H5 方案2H5页面小程序开发 方案3H5页面小程序页面 使用H5来开发APP&#xff0c;之后将该H…

TG7050CKN,TG7050SKN ,TG7050CMN,TG7050SMN

爱普生推出的温补晶振型号&#xff1a;TG7050CKN&#xff0c;TG7050SKN &#xff0c;TG7050CMN&#xff0c;TG7050SMN频率范围为 10mhz ~ 54mhz 适用于广泛的频率需求。这几款的特点就是耐高温&#xff0c;温度可达105℃高温&#xff0c;而且都是高稳定性温补晶振&#xff0c;&…

Springboot单元测试mock踩坑

mock bean方式 //mock bean方式一MockBeanpublic UserMapper userMapper;//mock bean方式二Beanpublic LogMapper logMapper() {return Mockito.mock(LogMapper.class);} 如何注册mapstruct bean //扫描mapstruct包路径ComponentScan("xxx.xxx.mapstruct")public cl…

ERP与智能商品系统在供应链管理上有哪些区别和优势?

ERP系统和智能商品系统在供应链管理方面有以下区别和优势&#xff1a; 范围和综合性&#xff1a;ERP系统涵盖了企业的整个供应链管理过程&#xff0c;包括供应商管理、采购管理、库存管理、生产计划和物流管理等。它可以实现供应链上下游的信息共享和协同&#xff0c;提高供应…

计算机网络复习2

物理层 文章目录 物理层通讯基础奈奎斯特定理香农定理编码与调制交换传输介质&#xff08;了解&#xff09;物理层设备 通讯基础 数据信号码元信源信道信宿单工通道&#xff1a;只有一个方向半双工通道&#xff1a;不能同时发送和接收全双工通道 奈奎斯特定理 规定&#xff…

c++ map unordered_map 区别

目录 map访问,没有key会获取默认值 std::map 和 std::unordered_map 区别 map访问,没有key会获取默认值 在 C++ 的 std::map 中,如果使用 operator[] 访问一个不存在的键,它会创建一个新的键,并将其关联的值初始化为该值类型的默认值。如果值类型是内置类型(例如 int、…

下载完redis每次启动项目必须打开redis服务,否则不能运行,解决方法

redis-server.exe --service-install redis.windows.conf 在redis的目录启动终端运行此命令可以下载redis服务&#xff0c;然后在服务里面启动redis服务&#xff0c;之后就可以不用打开小黑框再启动了 redis下载地址&#xff1a; Redis下载安装教程_redis 3.2下载-CSDN博客

MFC:如何将JPEG等图片显示到对话框客户区

步骤: 0、打开VS2022创建一个基于对话框的MFC应用&#xff0c;项目名称命名为PicShow&#xff0c;创建完成后将对话框客户区中的"确定"按钮等内容删除&#xff08;具体步骤略&#xff09;。 1、建立菜单栏&#xff1a;文件->打开、退出。具体步骤&#x…

【C++核心编程(一)】

一、内存分区模型 C程序在执行时&#xff0c;将内存大方向划分为4个区域&#xff1a; 代码区&#xff1a;存放函数体的二进制代码&#xff0c;由操作系统进行管理的。 全局区&#xff1a;存放全局变量和静态变量以及常量。 栈区&#xff1a;由编译器自动分配释放,存放函数的…