JS设计模式指南

什么是设计模式

简单来说,设计模式就是解决一类问题通用且可复用的方案。它不是具体的代码,而是一种思想,一种经验总结。我们平时写代码时,设计模式就是我们写代码的结构和"地基",我们要按照这个特定模式来写。因此,在编程中,设计模式就是帮我们写出更健壮且易于维护的代码。

常见设计模式如下:

单例模式

  1. 核心思想:保证一个类只有一个实例,并提供一个全局访问点

试想,你有一个全局的配置管理器,你希望在整个应用中,无论在哪里都只创建一个实例,而不是每次调用都创建一个新的,这就是单例模式的用武之地。

  1. 怎么实现:用一个变量来缓存这个已经创建好的实例

    javascript 复制代码
    class Singleton {
      constructor() {
        // 假设这里有一些初始化逻辑
        console.log('我被创建了!');
      }
    
      // 核心:静态方法或属性来管理实例
      static getInstance() {
        // 检查是否已经存在实例
        if (!Singleton.instance) {
          // 如果不存在,就创建一个新的并缓存起来
          Singleton.instance = new Singleton();
        }
        // 返回缓存的实例
        return Singleton.instance;
      }
    
      // 假设有一些方法
      showMessage() {
        console.log('我是单例模式的实例!');
      }
    }
    
    // 第一次获取实例
    const instance1 = Singleton.getInstance(); 
    // 输出: "我被创建了!"
    
    // 第二次获取实例
    const instance2 = Singleton.getInstance();
    // 不会再输出 "我被创建了!"
    
    // 比较两个实例,它们是同一个!
    console.log(instance1 === instance2); // 输出: true
    
    instance1.showMessage();
    instance2.showMessage();
  2. 总结:单例模式就像一个独一无二的总司令,所有人只能通过一个固定的渠道(getInstance方法)来和他联系,确保不会有第二个总司令出现。

工厂模式

  1. 核心思想:定义一个创建对象的接口,但让子类决定实例化哪一个类。

这个模式就像一个工厂。你告诉工厂你想要一个什么产品,工厂就会帮你生产出来,但不需要知道具体的生产细节。

  1. 怎么实现:我们创建一个工厂函数或工厂类,专门负责创建不同类型的对象。

    typescript 复制代码
    // 假设我们有不同类型的用户
    class RegularUser {
      constructor() {
        this.type = '普通用户';
      }
    }
    
    class AdminUser {
      constructor() {
        this.type = '管理员';
      }
    }
    
    // 工厂函数:根据传入的类型创建不同的用户对象
    function UserFactory(type) {
      switch (type) {
        case 'regular':
          return new RegularUser();
        case 'admin':
          return new AdminUser();
        default:
          throw new Error('不支持的用户类型!');
      }
    }
    
    // 生产一个普通用户
    const user1 = UserFactory('regular');
    console.log(user1.type); // 输出: "普通用户"
    
    // 生产一个管理员
    const user2 = UserFactory('admin');
    console.log(user2.type); // 输出: "管理员"
  2. 总结:工厂模式把创建对象的逻辑和使用对象的逻辑分开了。当你需要创建许多相似但又有点不同的对象时,它可以使代码更整洁的同时且易于扩展。

观察者模式

  1. 核心思想:定义了对象之间一对多的关系,当一个对象状态发生改变时,所有依赖它的对象都会得到通知并自动更新。

这个模式就像公众号推送文章。公众号(主题)推送一篇文章,所有订阅了这个公众号的读者(观察者)都会收到这篇文章。

  1. 怎么实现:需要一个主题来管理观察者,以及一些观察者。

主题:负责添加,移除,通知观察者。

观察者:订阅主题,并提供一个更新方法。

  1. 总结

观察者模式在前端应用中非常常见,比如事件监听(addEventListener).DOM元素就是主题,你注册的回调函数就是观察者。当事件发生时,DOM元素会通知所有注册的函数执行。

代理模式

  1. 核心思想:为另一个对象提供一个替身或占位符,以控制对这个对象的访问。

类比于明星与经纪人,艺人所有事物都由经纪人处理,外部只和经纪人打交道,经纪人决定是否让外部接触到明星

  1. 怎么实现:创建一个代理对象,它和真实对象实现相同的接口,但在真实调用对象的方法前后,以增加额外的逻辑

    javascript 复制代码
    // 1. 定义真实对象 (明星)
    class Star {
      constructor(name) {
        this.name = name;
      }
    
      // 假设这是明星接广告的方法
      receiveAd(ad) {
        console.log(`${this.name} 正在拍摄广告: ${ad}`);
      }
    }
    
    // 2. 定义代理对象 (经纪人)
    class Agent {
      constructor(star) {
        // 代理对象内部持有真实对象的引用
        this.star = star;
      }
    
      // 代理方法:在调用真实对象的方法前后增加逻辑
      receiveAd(ad) {
        // 前置逻辑:代理人先筛选广告
        if (ad === '劣质广告') {
          console.log('经纪人拒绝了劣质广告!');
          return;
        }
        
        console.log(`经纪人正在为 ${this.star.name} 接洽广告...`);
        
        // 调用真实对象的方法
        this.star.receiveAd(ad);
        
        // 后置逻辑:广告拍摄后,代理人处理后续事务
        console.log('经纪人处理了广告尾款和宣传事宜。');
      }
    }
    
    // --- 使用代理模式 ---
    
    const jayChou = new Star('周杰伦');
    const jayChouAgent = new Agent(jayChou);
    
    // 通过经纪人(代理)来接广告
    jayChouAgent.receiveAd('优质饮品广告');
    // 输出:
    // 经纪人正在为 周杰伦 接洽广告...
    // 周杰伦 正在拍摄广告: 优质饮品广告
    // 经纪人处理了广告尾款和宣传事宜。
    
    console.log('---')
    
    // 经纪人拒绝劣质广告
    jayChouAgent.receiveAd('劣质广告');
    // 输出:
    // 经纪人拒绝了劣质广告!
  2. 总结:代理模式关注的是对对象的控制和保护。我需要对一个对象的访问进行控制(在访问的前后做一些事),所以用一个代理包裹它。

装饰器模式

  1. 核心思想:动态地给一个对象添加额外的功能。

想象一下,你有一个基础的咖啡(被装饰对象)。你想给它加点东西,比如牛奶,糖,奶油,你不需要改变咖啡本身的结构,而是用装饰器来包裹它,从而动态地添加新功能

  1. 怎么实现:创建一系列装饰器,它们都继承自统一接口,并且都持有一个被装饰对象的引用。每个装饰器在调用被装饰对象的方法后,再添加自己的功能。

    javascript 复制代码
    // 1. 定义基础组件 (咖啡)
    class Coffee {
      cost() {
        return 10;
      }
      getDescription() {
        return '一杯纯咖啡';
      }
    }
    
    // 2. 定义装饰器基类 (通常是抽象类或接口,JS中可以用普通类模拟)
    // 装饰器基类应该和Coffee有相同的接口
    // class Decorator {
    //    cost() { ... }
    //    getDescription() { ... }
    // }
    
    // 3. 定义具体的装饰器 (牛奶, 糖, 奶油)
    
    // 牛奶装饰器
    class MilkDecorator {
      constructor(coffee) {
        this.coffee = coffee; // 持有被装饰对象的引用
      }
    
      cost() {
        // 在原有价格上加上牛奶的价格
        return this.coffee.cost() + 3;
      }
    
      getDescription() {
        // 在原有描述上加上牛奶
        return this.coffee.getDescription() + ', 加牛奶';
      }
    }
    
    // 糖装饰器
    class SugarDecorator {
      constructor(coffee) {
        this.coffee = coffee;
      }
    
      cost() {
        return this.coffee.cost() + 2;
      }
    
      getDescription() {
        return this.coffee.getDescription() + ', 加糖';
      }
    }
    
    // --- 使用装饰器模式 ---
    
    let myCoffee = new Coffee(); // 制作一杯纯咖啡
    
    console.log(myCoffee.getDescription(), myCoffee.cost()); // 输出: 一杯纯咖啡 10
    
    // 给咖啡加上牛奶
    myCoffee = new MilkDecorator(myCoffee);
    console.log(myCoffee.getDescription(), myCoffee.cost()); // 输出: 一杯纯咖啡, 加牛奶 13
    
    // 再给这杯加了牛奶的咖啡加上糖
    myCoffee = new SugarDecorator(myCoffee);
    console.log(myCoffee.getDescription(), myCoffee.cost()); // 输出: 一杯纯咖啡, 加牛奶, 加糖 15
    
    // ES7+ 的 Decorator 语法 (更简洁,但目前仍是提案)
    // @milk
    // @sugar
    // class MyCoffee {
    //   ...
    // }
  2. 总结:装饰器模式通过组合而非继承的方式来扩展对象的功能。它可以避免创建一个复杂的继承体系。这种方式非常灵活,可以动态地,一层一层地给对象添加功能。像react框架中,高阶组件就是一种典型地装饰器模式体现。

总结思考

设计模式的精髓在于解耦(降低模块间的耦合度)和扩展(让代码更容易添加新功能)。当你面对一个具体问题时,试着思考:"我可以用什么模式解决"。这样就可以把理论知识转为实际的编码能力。

相关推荐
Mintopia2 小时前
🚀 Next.js 全栈 E2E 测试:Playwright vs Cypress
前端·javascript·next.js
拳打南山敬老院2 小时前
漫谈 MCP 构建之Resources篇
前端·后端·ai编程
golang学习记2 小时前
从0死磕全栈第九天:Trae AI IDE一把梭,使用react-query快速打通前后端接口调试
前端
超人9212 小时前
我用纯前端技术打造了一个开发者工具箱,10+实用工具助力提效!
前端
bug_kada2 小时前
详解 React useCallback & useMemo
前端·react.js
Mintopia2 小时前
⚡ WebAssembly 如何加速 AIGC 模型在浏览器中的运行效率?
前端·javascript·aigc
AAA_Tj2 小时前
前端动画技术全景指南:四大动画技术介绍
前端
断竿散人2 小时前
乾坤微前端框架的沙箱技术实现原理深度解析
前端·javascript·前端框架
进阶的鱼2 小时前
(4种场景)单行、多行文本超出省略号隐藏
前端·css·面试