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

06-02 1389阅读

目录

高级前端工程师必备的 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 设计模式入门教程,常用设计模式案例分享

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

高级前端工程师必备的 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全面指南:从基础到进阶,掌握容器化构建的核心工具

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码