Skip to content

设计模式面试题精选

汇总单例、观察者、工厂、策略、代理、装饰器等高频设计模式面试题。

基础概念

1. 什么是设计模式?前端常用哪些?

设计模式是解决软件设计中常见问题的可复用方案,分三大类:

  • 创建型:单例、工厂、抽象工厂、建造者、原型
  • 结构型:代理、装饰器、适配器、外观、组合
  • 行为型:观察者、策略、命令、迭代器、模板方法

前端最常用:单例、观察者/发布订阅、工厂、策略、代理、装饰器


单例模式

2. 单例模式的实现和应用场景?

确保一个类只有一个实例,并提供全局访问点。

javascript
class Singleton {
    static instance = null;

    static getInstance() {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    }
}

// 更简洁的模块单例(ES Module 天然单例)
// store.js
export const store = { state: {} }; // 模块只加载一次

前端应用场景:全局状态管理(Vuex/Pinia store)、全局事件总线、弹窗管理器、WebSocket 连接、日志系统


观察者模式 vs 发布订阅模式

3. 观察者模式和发布订阅模式的区别?
特性观察者模式发布订阅模式
耦合度观察者和被观察者直接依赖通过事件中心解耦,完全不知道对方
通信直接调用观察者方法通过事件名通信
典型实现Vue 响应式、DOM 事件EventBus、Node.js EventEmitter、Redux
javascript
// 手写 EventEmitter(高频手写题)
class EventEmitter {
    constructor() { this.events = {}; }

    on(event, listener) {
        (this.events[event] ??= []).push(listener);
        return this;
    }

    off(event, listener) {
        this.events[event] = this.events[event]?.filter(l => l !== listener);
        return this;
    }

    emit(event, ...args) {
        this.events[event]?.forEach(l => l(...args));
        return this;
    }

    once(event, listener) {
        const wrapper = (...args) => { listener(...args); this.off(event, wrapper); };
        return this.on(event, wrapper);
    }
}

工厂模式

4. 工厂模式有哪几种?各有什么区别?
类型说明适用场景
简单工厂一个函数/类根据参数创建不同对象创建逻辑简单,类型固定
工厂方法定义创建接口,子类决定实例化哪个类需要扩展新类型,符合开闭原则
抽象工厂创建一系列相关对象的接口产品族(如不同主题的 UI 组件套件)
javascript
// 简单工厂(前端最常用)
function createComponent(type, props) {
    const map = { button: Button, input: Input, select: Select };
    const Component = map[type];
    if (!Component) throw new Error(`Unknown type: ${type}`);
    return new Component(props);
}

// 工厂方法(Vue/React 组件注册本质)
class ComponentFactory {
    create(props) { throw new Error('子类必须实现'); }
}
class ButtonFactory extends ComponentFactory {
    create(props) { return new Button(props); }
}

策略模式

5. 策略模式的应用场景?

将算法封装成独立的策略类,使它们可以互相替换,消除大量 if-else。

javascript
// ❌ 不用策略模式
function getDiscount(type, price) {
    if (type === 'vip') return price * 0.8;
    if (type === 'svip') return price * 0.6;
    if (type === 'normal') return price;
}

// ✅ 策略模式
const discountStrategies = {
    vip: price => price * 0.8,
    svip: price => price * 0.6,
    normal: price => price,
};

function getDiscount(type, price) {
    const strategy = discountStrategies[type];
    if (!strategy) throw new Error(`Unknown type: ${type}`);
    return strategy(price);
}

前端应用:表单验证规则、权限控制、动画策略、排序算法切换


代理模式

6. 代理模式在前端的应用?

为对象提供一个代理,控制对该对象的访问。

javascript
// ES6 Proxy(Vue 3 响应式的核心)
const handler = {
    get(target, key) {
        console.log(`读取 ${key}`);
        return Reflect.get(target, key);
    },
    set(target, key, value) {
        console.log(`设置 ${key} = ${value}`);
        return Reflect.set(target, key, value);
    }
};
const proxy = new Proxy({}, handler);

// 虚拟代理:图片懒加载
class ImageProxy {
    constructor(src) {
        this.realImage = null;
        this.src = src;
        this.placeholder = 'loading.gif';
    }
    display() {
        if (!this.realImage) {
            this.realImage = new RealImage(this.src);
        }
        return this.realImage.display();
    }
}

前端应用:Vue 3 响应式(Proxy)、图片懒加载、缓存代理、权限拦截、API 请求拦截


装饰器模式

7. 装饰器模式和继承的区别?

装饰器在不修改原对象的情况下动态添加功能,比继承更灵活(组合优于继承)。

javascript
// 函数装饰器(高阶函数)
function withLogging(fn) {
    return function(...args) {
        console.log(`调用 ${fn.name},参数:`, args);
        const result = fn.apply(this, args);
        console.log(`返回:`, result);
        return result;
    };
}

const add = (a, b) => a + b;
const loggedAdd = withLogging(add);

// TypeScript/ES 装饰器(类装饰器)
function readonly(target, key, descriptor) {
    descriptor.writable = false;
    return descriptor;
}

class Circle {
    @readonly
    getArea() { return Math.PI * this.r ** 2; }
}

前端应用:React HOC(高阶组件)、Vue mixins、AOP(日志、权限、缓存)、TypeScript 装饰器(NestJS)


综合题

8. SOLID 原则是什么?
原则说明前端示例
S 单一职责一个类只做一件事组件只负责 UI,逻辑抽到 hooks
O 开闭原则对扩展开放,对修改关闭策略模式添加新策略不改旧代码
L 里氏替换子类可替换父类组件继承保持接口兼容
I 接口隔离不依赖不需要的接口TypeScript 接口拆分
D 依赖倒置依赖抽象而非具体依赖注入(NestJS)