高级前端工程师必备的 JS 设计模式入门教程,常用设计模式案例分享

目录

高级前端工程师必备的 JS 设计模式入门教程,常用设计模式案例分享

一、什么是设计模式?为什么前端也要学?

1、设计模式是什么

2、设计模式的产出

二、设计模式在 JS 里的分类

三、常用设计模式实战讲解

1、单例模式(Singleton)

2、工厂模式(Factory)

3、观察者模式(Observer)

4、代理模式(Proxy)

5、策略模式(Strategy)

6、建造者模式、适配器模式、装饰器模式和状态模式

①建造者模式(Builder Pattern)

②适配器模式(Adapter Pattern)

③装饰器模式(Decorator Pattern)

④状态模式(State Pattern)

四、结语


        作者:watermelo37

        CSDN万粉博主、华为云云享专家、阿里云专家博主、腾讯云、支付宝合作作者,全平台博客昵称watermelo37。

        一个假装是giser的coder,做不只专注于业务逻辑的前端工程师,Java、Docker、Python、LLM均有涉猎。

---------------------------------------------------------------------

温柔地对待温柔的人,包容的三观就是最大的温柔。

---------------------------------------------------------------------

高级前端工程师必备的 JS 设计模式入门教程,常用设计模式案例分享

本文适合有JavaScript基础 、 Vue/React 开发经验,但对设计模式不太熟悉的前端开发工程师。

        什么是设计模式?为什么要有设计模式?单例模式、工厂模式、建造者模式、适配器模式、代理模式、装饰器模式、观察者模式、策略模式、状态模式分别是什么?他们解决了什么样的问题?本文将给您答案。

一、什么是设计模式?为什么前端也要学?

1、设计模式是什么

        设计模式,就是程序员们总结出来的一套解决常见代码问题的方法论

  • 它不是死板的规定,不是必须按部就班执行。

  • 它是经验总结,告诉你在遇到某些类型的问题时,如何写出更优雅、更健壮、更可扩展的代码。

  • 后端用,前端也用,尤其是当你的项目越来越大、多人协作越来越复杂时,设计模式就越重要。

        类比一下:设计模式就像打游戏时的「固定套路」,让你在遇到不同怪物时不用每次重头想招数,而是有一套拿得出手的打法。

2、设计模式的产出

        设计模式的产出不是特定代码片段,而是一套可复用的设计思路或结构

  • 有些模式产出一个「结构」(比如单例产出一个唯一对象)。

  • 有些模式产出一个「流程/机制」(比如观察者产出的是发布-订阅机制)。

  • 也有些是组合型的,既有对象也有通信逻辑。

        设计模式产出的是「结构 + 行为」的组合,没有严格固定形式,但背后有固定的“意图”或“问题场景”。我们关注的就是其“意图”和“场景”。

二、设计模式在 JS 里的分类

        设计模式很多,但最重要的,可以分成三大类:

类型解释代表模式
创建型解决「如何创建对象」的问题单例模式、工厂模式、建造者模式
结构型解决「对象之间怎么组织结构」的问题适配器模式、代理模式、装饰器模式
行为型解决「对象之间怎么协作通信」的问题观察者模式、策略模式、状态模式

三、常用设计模式实战讲解

1、单例模式(Singleton)

        定义:整个系统只存在一个实例对象,并且能全局访问。

        举个例子,Vuex里面的store就是单例,事件总线(Event Bus)也是单例。

        来做个简单的实现,它通过一个静态属性 instance 储存实例,并在重复创建时返回已有实例,这样就能实现单例的效果:

class Store {constructor() {if (Store.instance) {return Store.instance;}this.state = {};Store.instance = this;}
}const a = new Store();
const b = new Store();console.log(a === b); // true

2、工厂模式(Factory)

        定义:用一个函数/类专门负责创建对象,不直接用 new。

        举几个例子,组件封装:根据类型不同返回不同组件实例还有 Axios 的实例化都是工厂模式。

        工厂模式的优势在于能统一创建逻辑,并且拓展非常方便,各类型之间高内聚,低耦合。做个简单的实现:

function createButton(type) {if (type === 'primary') {return { color: 'blue', size: 'large' };} else if (type === 'danger') {return { color: 'red', size: 'large' };}
}const btn = createButton('primary');
console.log(btn); // { color: 'blue', size: 'large' }

工厂函数是什么? 

        工厂函数(Factory Function)是一个普通函数,它封装了创建对象的过程,并返回这个对象。它是实现工厂模式的一种方式,但不限于工厂模式使用。

function createPerson(name, age) {return {name,age,sayHello() {console.log(`Hi, I'm ${name}`);}}
}const person = createPerson('Tom', 20);

        这个案例中的 createPerson 就是工厂函数。

3、观察者模式(Observer)

        定义:一对多关系,一个对象变化,通知所有依赖它的对象。

        比如Vue 响应式,发布订阅(EventEmitter)都属于观察者模式。

        一般情况下,观察者模式里面一定有一个叫做 notify / emit / publish 或类似功能的函数,用来通知所有的订阅者,这种函数本质上就是广播机制,是观察者模式的核心实现函数。做个简单的案例:维护一组回调,变化时循环触发。

class Observer {constructor() {this.subscribers = [];}subscribe(fn) {this.subscribers.push(fn);}notify(data) {this.subscribers.forEach(fn => fn(data));}
}const obs = new Observer();obs.subscribe((data) => console.log('收到1', data));
obs.subscribe((data) => console.log('收到2', data));obs.notify('你好');
// 收到1 你好
// 收到2 你好

4、代理模式(Proxy)

        定义:通过一个代理对象控制对目标对象的访问。

        Vue3的响应式Proxy、接口请求封装都属于代理模式。

        先用Proxy举例:

const target = {name: '张三'
};const proxy = new Proxy(target, {get(obj, prop) {console.log(`访问属性:${prop}`);return obj[prop];},set(obj, prop, value) {console.log(`设置属性:${prop}=${value}`);obj[prop] = value;return true;}
});proxy.name; // 访问属性:name
proxy.age = 20; // 设置属性:age=20

        这个例子比较常见,是不是意犹未尽?我们再来一个图片懒加载的代理模式来举例:

// 真正加载图片的方法
function loadImage(src) {const img = new Image();img.src = src;document.body.appendChild(img);
}// 创建一个代理来控制图片加载
const proxyImage = (function () {let img = new Image();img.onload = function() {loadImage(this.src); // 真正加载}return function(src) {// 先用一张 loading 图片占位loadImage('loading.jpg');// 真正的图片异步加载完成后替换img.src = src;}
})();// 使用
proxyImage('real-image.jpg');

        proxyImage 函数就是代理对象,loadImage是被代理的目标对象,用户一调用它就显示 loading 图,真正图片在加载完毕后替换。

        这里使用 IIFE(立即执行函数表达式) 是为了创建一次性的私有作用域和闭包,隔离 img 变量,避免污染。

        执行逻辑为:

  1. 代码刚执行时,生成了一个 proxyImage 函数,这个函数内部封装了一个私有的 Image 对象 img,并且给 img.onload 绑定了回调函数。

  2. 调用 proxyImage(src) 时,先 loadImage('loading.jpg') 显示一个占位图,再设置 img.src = src(真实图片地址)。

  3. 这时浏览器会开始异步加载 src 指向的真实图片,这一步是静默执行的,DOM渲染的是默认图片。

  4. 加载完毕后自动触发 img.onload,在回调里重新调用 loadImage(this.src),替换成真实图。

        其中,默认图片(loading.jpg)应该是一个极小的图,通过base64编码内嵌到代码里,或者直接用svg小图标,如果是个大一点的图片应该让浏览器预加载,以此来实现用户视角下的默认图片无感渲染。

5、策略模式(Strategy)

        定义:将一系列算法封装成独立的策略类,使它们可以互相替换。

        比如一些表单验证、支付方式切换过程等。

        其优势在于具有开闭原则(增加新策略不用改老代码),并且代码可扩展。举例如下,其中strategies 里是具体的策略集合(算法实现),validate 是「上下文」调用器(根据情况选择策略执行),两者配合才构成完整的策略模式实现:

const strategies = {isNonEmpty(value) {return value !== '';},minLength(value, length) {return value.length >= length;}
};function validate(value, rule, ...args) {return strategies[rule](value, ...args);
}console.log(validate('abc', 'minLength', 2)); // true

开闭原则:

        即对扩展开放,对修改关闭。

        产生新需求时,应该通过新增代码来实现,而不是修改现有代码。这样可以减少出错的风险,提高系统的稳定性。

        在策略模式中,策略模式增加新策略,只是添加新策略对象,不需要去改 validate 函数本体

        有没有觉得策略模式和工厂模式很像?都是通过一个“judge info”(type与rule)来判断到底该如何执行。但他们存在以下区别:

策略模式工厂模式
关注执行逻辑不同关注对象创建不同
替换算法/规则替换对象
运行时切换生成时选择

6、建造者模式、适配器模式、装饰器模式和状态模式

        这四个相对少见,简单介绍:

①建造者模式(Builder Pattern)

        分步骤构建一个复杂对象,而不是一口气构造。比如建一台电脑:

class ComputerBuilder {constructor() {this.computer = {};}addCPU(cpu) {this.computer.cpu = cpu;return this;}addRAM(ram) {this.computer.ram = ram;return this;}addStorage(storage) {this.computer.storage = storage;return this;}build() {return this.computer;}
}const myComputer = new ComputerBuilder().addCPU('Intel i9').addRAM('32GB').addStorage('1TB SSD').build();
②适配器模式(Adapter Pattern)

        让原本接口不兼容的两个类可以一起工作。比如一个老版 API 返回的是 snake_case,但新系统要求 camelCase:

function oldApi() {return { user_name: 'Tom', user_age: 20 };
}// 适配器
function adapterApi() {const data = oldApi();return {userName: data.user_name,userAge: data.user_age};
}const result = adapterApi();
③装饰器模式(Decorator Pattern)

        在不修改原对象的情况下,动态增加功能。比如增强 log 函数(这里相当于额外打印了时间):

function log(msg) {console.log(msg);
}// 装饰器
function withTimestamp(fn) {return function(...args) {console.log(`[${new Date().toISOString()}]`);return fn(...args);}
}const decoratedLog = withTimestamp(log);
decoratedLog('Hello World');
④状态模式(State Pattern)

        对象内部状态不同,行为也不同。比如电灯开关:

class Light {constructor() {this.state = 'off';}toggle() {if (this.state === 'off') {console.log('Turning on');this.state = 'on';} else {console.log('Turning off');this.state = 'off';}}
}const light = new Light();
light.toggle(); // Turning on
light.toggle(); // Turning off

四、结语

        设计模式能让人写出更稳定、可维护的代码,JavaScript中最常用的设计模式有单例、工厂、观察者、代理、策略五种。学设计模式就相当于学解决复杂问题的套路,在框架中,如 Vue / React项目里,几乎随处可见设计模式的身影。

        只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

        其他热门文章,请关注:

        极致的灵活度满足工程美学:用Vue Flow绘制一个完美流程图

        你真的会使用Vue3的onMounted钩子函数吗?Vue3中onMounted的用法详解

        DeepSeek:全栈开发者视角下的AI革命者

        通过array.filter()实现数组的数据筛选、数据清洗和链式调用

        通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能

        TreeSize:免费的磁盘清理与管理神器,解决C盘爆满的燃眉之急

        通过MongoDB Atlas 实现语义搜索与 RAG——迈向AI的搜索机制

        深入理解 JavaScript 中的 Array.find() 方法:原理、性能优势与实用案例详解

        el-table实现动态数据的实时排序,一篇文章讲清楚elementui的表格排序功能

        MutationObserver详解+案例——深入理解 JavaScript 中的 MutationObserver

        JavaScript中通过array.map()实现数据转换、创建派生数组、异步数据流处理、DOM操作等

        前端实战:基于Vue3与免费满血版DeepSeek实现无限滚动+懒加载+瀑布流模块及优化策略

        高效工作流:用Mermaid绘制你的专属流程图;如何在Vue3中导入mermaid绘制流程图

        干货含源码!如何用Java后端操作Docker(命令行篇)

        在线编程实现!如何在Java后端通过DockerClient操作Docker生成python环境

        Dockerfile全面指南:从基础到进阶,掌握容器化构建的核心工具

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

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

相关文章

Ubuntu+Docker+内网穿透:保姆级教程实现安卓开发环境远程部署

文章目录 前言1. 虚拟化环境检查2. Android 模拟器部署3. Ubuntu安装Cpolar4. 配置公网地址5. 远程访问小结 6. 固定Cpolar公网地址7. 固定地址访问 前言 本文将详细介绍一种创新性的云开发架构:基于Ubuntu系统构建Android仿真容器环境,并集成安全隧道技…

Linux Kernel调试:强大的printk(一)

引言 想了好久,还是觉得这个标题才配得上printk!^_^ 我相信,不管做什么开发,使用最多的调试手段应该就是打印了,从我们学习编程语言第一课开始,写的第一段代码,就是打印"Hello, world&qu…

基于NLP技术的客户投诉与需求文本分类方法研究

目录 摘要 1. 引言 2. 文本分类基础 2.1 文本分类的定义与类型 2.2 文本分类的评价指标 3. 传统文本分类方法 3.1 基于TF-IDF和SVM的方法 3.2 基于主题模型和词向量的改进方法 4. 深度学习文本分类方法 4.1 TextCNN模型 4.2 BiLSTM模型 4.3 注意力机制与Transformer…

#RabbitMQ# 消息队列入门

目录 一 MQ技术选型 1 运行rabbitmq 2 基本介绍 3 快速入门 1 交换机负责路由消息给队列 2 数据隔离 二 Java客户端 1 快速入门 2 WorkQueue 3 FanOut交换机 4 Direct交换机 5 Topic交换机 *6 声明队列交换机 1 在配置类当中声明 2 使用注解的方式指定 7 消息转…

【深度学习】多目标融合算法(六):渐进式分层提取模型PLE(Progressive Layered Extraction)

目录 一、引言 二、PLE(Progressive Layered Extraction,渐进式分层提取模型) 2.1 技术原理 2.2 技术优缺点 2.3 业务代码实践 2.3.1 业务场景与建模 2.3.2 模型代码实现 2.3.3 模型训练与推理测试 2.3.4 打印模型结构 三、总结 一…

【Java开发日记】如何使用Java开发在线生成 pdf 文档

一、介绍 在实际的业务开发的时候,研发人员往往会碰到很多这样的一些场景,需要提供相关的电子凭证信息给用户,例如网银/支付宝/微信购物支付的电子发票、订单的库存打印单、各种电子签署合同等等,以方便用…

Oracle 11g 单实例使用+asm修改主机名导致ORA-29701 故障分析

解决 把服务器名修改为原来的,重启服务器。 故障 建表空间失败。 分析 查看告警日志 ORA-1119 signalled during: create tablespace splex datafile ‘DATA’ size 2000M… Tue May 20 18:04:28 2025 create tablespace splex datafile ‘DATA/option/dataf…

消息队列的使用

使用内存队列来处理基于内存的【生产者-消费者】场景 思考和使用Disruptor Disruptor可以实现单个或多个生产者生产消息,单个或多个消费者消息,且消费者之间可以存在消费消息的依赖关系 使用Disruptor需要结合业务特性,设计要灵活 什么业务…

《帝国时代1》游戏秘籍

资源类 PEPPERONI PIZZA:获得 1000 食物。COINAGE:获得 1000 金。WOODSTOCK:获得 1000 木头。QUARRY:获得 1000 石头。 建筑与生产类 STEROIDS:快速建筑。 地图类 REVEAL MAP:显示所有地图。NO FOG&#xf…

使用JSP踩过的坑

虽然说jsp已经过时了,但是有时维护比较老的项目还是需要的。 下面说下,我使用jsp踩过的坑: 1.关于打印输出 在jsp中输出使用 out.println("hello");而不是 System.out.println("hello");如果在定义函数部分需要打印…

redis集群创建时手动指定主从关系的方法

适用场景: 创建主从关系时默认参数 --cluster-replicas 1 会自动分配从节点。 为了能精确控制 Redis Cluster 的主从拓扑结构,我们通过 Redis Cluster 的手动分片功能来实现 一、手动指定主从关系的方法 使用 redis-cli --cluster-replicas 0 先创建纯…

ROS合集(七)SVIn2声呐模块分析

文章目录 一、整体思想二、具体误差建模流程三、总结明确(预测值与观测值)四、选点逻辑五、Sonar 数据处理流水线1. ROS Launch 配置(imagenex831l.launch)2. SonarNode 节点(sonar_node.py)3. Subscriber …

Python爬虫实战:研究PySpider框架相关技术

1. 引言 1.1 研究背景与意义 网络爬虫作为互联网数据采集的重要工具,在信息检索、舆情分析、市场调研等领域发挥着重要作用。随着互联网信息的爆炸式增长,如何高效、稳定地获取所需数据成为了一个关键挑战。PySpider 作为一款功能强大的 Python 爬虫框架,提供了丰富的功能…

《大模型开源与闭源的深度博弈:科技新生态下的权衡与抉择》

开源智能体大模型的核心魅力,在于它构建起了一个全球开发者共同参与的超级协作网络。想象一下,来自世界各个角落的开发者、研究者,无论身处繁华都市还是偏远小镇,只要心怀对技术的热爱与追求,就能加入到这场技术狂欢中…

大数据模型对陌生场景图像的识别能力研究 —— 以 DEEPSEEK 私有化部署模型为例

摘要 本研究聚焦于已训练的大数据模型能否识别未包含在样本数据集中的陌生场景图像这一问题,以 DEEPSEEK 私有化部署模型为研究对象,结合机器学习理论,分析模型识别陌生场景图像的影响因素,并通过理论探讨与实际应用场景分析&…

STM32——从点灯到传感器控制

STM32基础外设开发:从点灯到传感器控制 一、前言 本篇文章总结STM32F10x系列基础外设开发实例,涵盖GPIO控制、按键检测、传感器应用等。所有代码基于标准库开发,适合STM32初学者参考。 二、硬件准备 STM32F10x系列开发板LED模块有源蜂鸣器…

[特殊字符] 使用增量同步+MQ机制将用户数据同步到Elasticsearch

在开发用户搜索功能时,我们通常会将用户信息存储到 Elasticsearch(简称 ES) 中,以提高搜索效率。本篇文章将详细介绍我们是如何实现 MySQL 到 Elasticsearch 的增量同步,以及如何通过 MQ 消息队列实现用户信息实时更新…

MyBatis缓存机制全解析

在MyBatis中,缓存分为一级缓存和二级缓存,它们的主要目的是减少数据库的访问次数,提高查询效率。下面简述这两种缓存的工作原理: 一、 一级缓存(SqlSession级别的缓存) 一级缓存是MyBatis默认开启的缓存机…

【短距离通信】【WiFi】WiFi7关键技术之4096-QAM、MRU

目录 3. 4096-QAM 3.1 4096-QAM 3.2 QAM 的阶数越高越好吗? 4. MRU 4.1 OFDMA 和 RU 4.2 MRU 资源分配 3. 4096-QAM 摘要 本章主要介绍了Wi-Fi 7引入的4096-QAM对数据传输速率的提升。 3.1 4096-QAM 对速率的提升 Wi-Fi 标准一直致力于提升数据传输速率&a…

【二刷力扣】【力扣热题100】今天的题目是:283.移动零

题目: 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0] 示例 2: 输…