你真的懂JavaScript设计模式吗?10个高频面试题揭开真相

第一章:JavaScript设计模式概述

JavaScript设计模式是开发者在长期实践中总结出的可复用解决方案,用于应对常见的软件设计问题。这些模式不提供具体的代码实现,而是描述了一种解决问题的思想和结构,帮助提升代码的可维护性、可扩展性和可读性。

设计模式的核心价值

  • 提高代码复用性,减少重复逻辑
  • 增强模块间的解耦,便于团队协作
  • 提供标准化的沟通语言,使架构讨论更高效

常见的设计模式分类

类型典型模式适用场景
创建型单例、工厂、构造器对象创建过程的封装与控制
结构型装饰器、适配器、代理对象组合或类继承的结构优化
行为型观察者、策略、命令对象间通信与职责分配

单例模式示例

// 确保一个类仅有一个实例,并提供全局访问点
class Singleton {
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;
    }
    this.data = 'I am the only instance';
    Singleton.instance = this;
  }

  getData() {
    return this.data;
  }
}

// 使用方式
const instance1 = new Singleton();
const instance2 = new Singleton();
console.log(instance1 === instance2); // true,引用同一实例
graph TD A[客户端请求实例] --> B{实例是否存在?} B -->|否| C[创建新实例并保存] B -->|是| D[返回已有实例] C --> E[返回实例] D --> E

第二章:创建型设计模式核心解析

2.1 工厂模式的实现与应用场景

工厂模式是一种创建型设计模式,用于在不指定具体类的情况下创建对象。它通过定义一个创建对象的接口,由子类决定实例化哪一个类,从而实现松耦合。
简单工厂示例
type Product interface {
    GetName() string
}

type ConcreteProductA struct{}

func (p *ConcreteProductA) GetName() string {
    return "Product A"
}

type SimpleFactory struct{}

func (f *SimpleFactory) CreateProduct(typ string) Product {
    if typ == "A" {
        return &ConcreteProductA{}
    }
    return nil
}
上述代码中,SimpleFactory 根据传入参数返回不同产品实例。调用者无需关心对象创建细节,仅依赖接口编程。
典型应用场景
  • 需要根据不同配置生成对象时
  • 对象创建逻辑复杂且需集中管理
  • 希望屏蔽产品类的具体实现

2.2 单例模式的惰性加载与全局控制

在高并发系统中,单例模式通过惰性加载(Lazy Initialization)实现资源的按需创建,有效降低启动开销。该机制确保实例仅在首次被请求时初始化,结合线程安全控制,保障唯一性。
双重检查锁定实现

public class LazySingleton {
    private static volatile LazySingleton instance;
    
    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}
上述代码中,volatile 关键字防止指令重排序,两次判空减少同步开销。首次调用时才创建实例,实现真正的惰性加载。
优势与适用场景
  • 节省内存资源,延迟初始化开销
  • 保证全局状态一致性
  • 适用于配置管理、日志服务等高频访问对象

2.3 建造者模式解耦对象构造过程

在复杂对象的创建过程中,若直接使用构造函数或工厂模式,容易导致参数膨胀与职责混乱。建造者模式通过将构造逻辑分步封装,实现对象构建过程的解耦。
核心结构与角色分工
  • Product:最终构建的复杂对象
  • Builder:定义构建步骤的抽象接口
  • ConcreteBuilder:实现具体构建逻辑
  • Director:指挥构建流程,隔离使用与构造
代码示例:构建HTTP请求配置
type RequestConfig struct {
    URL      string
    Timeout  int
    Headers  map[string]string
    Retries  int
}

type ConfigBuilder interface {
    SetURL(url string) ConfigBuilder
    SetTimeout(seconds int) ConfigBuilder
    AddHeader(key, value string) ConfigBuilder
    SetRetries(n int) ConfigBuilder
    Build() *RequestConfig
}
上述接口将配置项逐步设置,避免一次性传入大量可选参数。每个方法返回构建器自身,支持链式调用,提升代码可读性。最终由 Build() 方法完成对象组装,确保构造过程与表示分离。

2.4 原型模式利用克隆提升性能

原型模式通过复制现有对象来创建新实例,避免重复执行复杂初始化过程,显著提升性能。尤其在对象创建成本较高时,克隆机制尤为有效。
克隆实现方式
JavaScript 中可通过 Object.create() 或扩展运算符实现浅克隆:

const prototypeObj = { config: { timeout: 5000 }, init() { /* 初始化逻辑 */ } };
const clonedObj = Object.create(prototypeObj);
上述代码中,clonedObj 继承原型方法与属性,无需重新定义配置结构。
性能对比
创建方式耗时(ms)内存占用
构造函数初始化12.4
原型克隆2.1
克隆操作省去重复计算,适用于频繁实例化场景。

2.5 抽象工厂模式组织相关产品族

抽象工厂模式用于创建一组相关或相互依赖的对象,而无需指定具体类。它通过定义一个创建产品族的接口,使客户端与具体实现解耦。
核心结构
  • 抽象工厂(Abstract Factory):声明创建一系列产品的方法
  • 具体工厂(Concrete Factory):实现创建具体产品族的逻辑
  • 抽象产品(Abstract Product):定义产品类型的接口
  • 具体产品(Concrete Product):实际被创建的对象
代码示例
type GUIFactory interface {
    CreateButton() Button
    CreateCheckbox() Checkbox
}

type WinFactory struct{}

func (f *WinFactory) CreateButton() Button { return &WinButton{} }
func (f *WinFactory) CreateCheckbox() Checkbox { return &WinCheckbox{} }
上述代码定义了一个 GUI 抽象工厂,能够创建按钮和复选框。Windows 工厂实现了该接口,返回对应平台的具体控件实例,从而确保同一产品族内的对象协同工作。

第三章:结构型设计模式实战精讲

3.1 适配器模式兼容不匹配接口

在系统集成中,常遇到接口不兼容的问题。适配器模式通过封装现有接口,使其符合客户端期望的协议,实现无缝调用。
适配器结构设计
适配器模式包含三个核心角色:目标接口(Target)、被适配者(Adaptee)和适配器(Adapter)。适配器继承目标接口并持有被适配者实例,将请求转发并转换。
  • 目标接口:定义客户端使用的标准方法
  • 被适配者:已有但接口不兼容的类
  • 适配器:桥接两者,实现接口转换
代码示例与解析
type Target interface {
    Request()
}

type Adaptee struct{}

func (a *Adaptee) SpecificRequest() {
    fmt.Println("Adaptee执行特殊请求")
}

type Adapter struct {
    adaptee *Adaptee
}

func (a *Adapter) Request() {
    a.adaptee.SpecificRequest() // 转换调用
}
上述代码中,Adapter 实现了 Target 接口的 Request 方法,并在其内部调用 AdapteeSpecificRequest,完成接口语义的映射与兼容。

3.2 装饰器模式动态扩展功能特性

装饰器模式是一种结构型设计模式,允许在不修改原有对象代码的前提下,动态地添加功能。它通过组合的方式,将功能封装在装饰器类中,实现灵活的功能扩展。
基本实现原理
核心思想是定义一个公共接口,基础组件和装饰器均实现该接口,装饰器持有组件的实例,从而在调用前后增强行为。
type Service interface {
    Execute() string
}

type BasicService struct{}

func (b *BasicService) Execute() string {
    return "执行基础服务"
}

type LoggingDecorator struct {
    service Service
}

func (l *LoggingDecorator) Execute() string {
    log.Println("开始执行")
    result := l.service.Execute()
    log.Println("执行完成")
    return result
}
上述代码中,LoggingDecorator 包装了 Service 实例,在保留原逻辑的同时增加了日志能力。通过链式包装,可叠加多个装饰器,如缓存、权限校验等。
优势与适用场景
  • 避免类爆炸:相比继承,更轻量地组合功能
  • 运行时动态扩展:可根据条件决定是否启用某项装饰
  • 符合开闭原则:扩展开放,修改封闭

3.3 代理模式控制对象访问逻辑

代理模式通过引入中间层控制对目标对象的访问,适用于权限校验、延迟加载和日志记录等场景。
静态代理与动态代理对比
  • 静态代理:在编译期确定代理类,职责单一但扩展性差
  • 动态代理:运行时生成代理类,Java 中可通过 java.lang.reflect.Proxy 实现
Java 动态代理示例
public class AccessProxy implements InvocationHandler {
    private Object target;

    public AccessProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置权限检查");
        Object result = method.invoke(target, args);
        System.out.println("后置日志记录");
        return result;
    }
}
上述代码中,invoke 方法拦截所有方法调用,实现访问控制。参数 proxy 为代理实例,method 表示被调用的方法,args 为方法参数。

第四章:行为型模式深度剖析

4.1 观察者模式实现事件驱动机制

观察者模式是事件驱动架构的核心设计模式之一,它定义了对象间一对多的依赖关系,当一个对象状态改变时,所有依赖者都会收到通知。
核心结构与角色
该模式包含两个主要角色:**主题(Subject)** 和 **观察者(Observer)**。主题维护观察者列表,并在状态变化时主动通知它们。
  • Subject:管理观察者注册与通知
  • Observer:定义接收更新的接口
Go语言实现示例
type Subject struct {
    observers []func(string)
}

func (s *Subject) Attach(obs func(string)) {
    s.observers = append(s.observers, obs)
}

func (s *Subject) Notify(msg string) {
    for _, obs := range s.observers {
        obs(msg)
    }
}
上述代码中,Attach 方法用于注册回调函数,Notify 遍历所有观察者并传递事件消息,实现了松耦合的事件通信机制。

4.2 策略模式优化条件分支代码结构

在处理多重条件分支时,传统的 if-elseswitch-case 结构容易导致代码臃肿、难以维护。策略模式通过将不同算法封装为独立类,实现行为的动态切换。
核心结构设计
定义统一接口,各类策略实现该接口:

public interface DiscountStrategy {
    double calculate(double price);
}

public class NormalDiscount implements DiscountStrategy {
    public double calculate(double price) {
        return price * 0.9; // 9折
    }
}
上述代码中,DiscountStrategy 抽象出计算折扣的契约,各实现类封装具体逻辑。
上下文调用
使用上下文持有策略实例,运行时注入:

public class PricingContext {
    private DiscountStrategy strategy;

    public void setStrategy(DiscountStrategy strategy) {
        this.strategy = strategy;
    }

    public double execute(double price) {
        return strategy.calculate(price);
    }
}
通过依赖注入方式解耦条件判断与执行逻辑,提升扩展性。新增策略无需修改原有代码,符合开闭原则。

4.3 迭代器模式封装集合遍历逻辑

在复杂集合结构中,直接暴露内部数据组织方式会导致耦合度上升。迭代器模式通过统一接口封装遍历逻辑,使客户端无需了解底层存储细节。
核心接口设计
type Iterator interface {
    HasNext() bool
    Next() interface{}
}

type Collection interface {
    GetIterator() Iterator
}
上述接口分离了集合的访问与内部实现。HasNext 判断是否还有元素,Next 返回当前元素并移动指针。
典型应用场景
  • 树形结构的深度优先遍历封装
  • 数据库结果集的延迟加载处理
  • 多数据源合并遍历的统一视图
通过注入不同迭代器实现,同一集合可支持顺序、逆序或过滤遍历,提升扩展性。

4.4 状态模式管理对象状态转换流

状态模式通过封装对象的状态行为,使对象在其内部状态改变时改变其行为,从而解耦状态判断与业务逻辑。
核心结构与角色
  • Context:持有当前状态对象的实例;
  • State 接口:定义状态行为契约;
  • ConcreteState:实现特定状态下的行为。
代码示例
type State interface {
    Handle(context *Context)
}

type ConcreteStateA struct{}
func (s *ConcreteStateA) Handle(context *Context) {
    fmt.Println("State A handling")
    context.SetState(&ConcreteStateB{})
}
上述代码中,Handle 方法执行后自动切换状态,实现无缝流转。参数 context 允许状态修改上下文中的当前状态实例,驱动状态迁移。
状态转换表
当前状态触发事件下一状态
AHandleB
BHandleA

第五章:设计模式在现代前端架构中的演进与思考

随着前端工程化的发展,设计模式的应用已从传统的类继承模型转向更灵活的组合式架构。现代框架如 React 和 Vue 通过 Hooks 和 Composition API 推动了函数式模式的普及。
状态管理中的观察者模式演化
在 Redux 中,Store 的订阅机制是典型的观察者模式实现。通过 store.subscribe() 注册回调,组件可响应状态变化:

const store = createStore(rootReducer);
store.subscribe(() => {
  console.log('State updated:', store.getState());
});
如今,React 的 Context 与 useReducer 结合,提供了更轻量的替代方案,减少了模板代码。
组合模式驱动 UI 构建
React 的组件模型本质上是组合模式的实践。通过嵌套组件构建可复用的 UI 树结构:
  • Layout 组件封装通用布局逻辑
  • Card 组件作为容器承载不同内容子组件
  • HOC 或自定义 Hook 抽离交互行为
这种模式提升了 UI 的可维护性与扩展性。
依赖注入的函数式实现
传统 DI 模式在前端常被忽视,但通过工厂函数和配置对象可实现松耦合:

function createApiClient({ baseUrl, auth }) {
  return {
    get: (path) => fetch(`${baseUrl}${path}`, { headers: auth })
  };
}
// 测试时可轻松替换 mock 实现
模式适用场景典型框架支持
观察者状态更新通知Redux, RxJS
组合UI 结构构建React, Vue
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值