🏗️
设计模式
7 道题目
难度筛选:
单例模式:保证一个类只有一个实例,并提供全局访问点。
JS 实现:
更简洁(利用模块缓存):
前端应用:
• Vuex/Pinia 的 Store
• 全局弹窗 / Toast 组件
• WebSocket 连接管理
• localStorage 封装
JS 实现:
class Singleton {
static instance;
constructor() {
if (Singleton.instance) return Singleton.instance;
Singleton.instance = this;
}
}更简洁(利用模块缓存):
// config.js
const config = { apiUrl: '...' };
export default config; // ES Module 天然单例前端应用:
• Vuex/Pinia 的 Store
• 全局弹窗 / Toast 组件
• WebSocket 连接管理
• localStorage 封装
查看答案即标记为已答
观察者模式:目标和观察者直接关联。
发布订阅模式:通过事件中心解耦。
核心区别:
• 观察者:Subject 和 Observer 直接知道对方
• 发布订阅:Publisher 和 Subscriber 通过 Event Broker 解耦
前端应用:Vue 响应式(观察者)、EventBus(发布订阅)、Redux。
class Subject {
observers = [];
subscribe(fn) { this.observers.push(fn); }
notify(data) { this.observers.forEach(fn => fn(data)); }
}发布订阅模式:通过事件中心解耦。
class EventBus {
events = {};
on(event, fn) { (this.events[event] ??= []).push(fn); }
emit(event, data) { this.events[event]?.forEach(fn => fn(data)); }
}核心区别:
• 观察者:Subject 和 Observer 直接知道对方
• 发布订阅:Publisher 和 Subscriber 通过 Event Broker 解耦
前端应用:Vue 响应式(观察者)、EventBus(发布订阅)、Redux。
查看答案即标记为已答
代理模式:为对象提供一个代理,控制对其的访问。
ES6 Proxy 实现:
前端应用:
1. Vue 3 响应式:Proxy 代理对象实现数据劫持
2. API 缓存代理:缓存请求结果
3. 权限控制:代理对象控制属性访问权限
4. 图片懒加载:先加载占位图,合适时机加载真实图片
ES6 Proxy 实现:
const handler = {
get(target, key) {
console.log('访问了', key);
return target[key];
},
set(target, key, value) {
console.log('设置了', key, value);
target[key] = value;
return true;
}
};
const proxy = new Proxy(obj, handler);前端应用:
1. Vue 3 响应式:Proxy 代理对象实现数据劫持
2. API 缓存代理:缓存请求结果
3. 权限控制:代理对象控制属性访问权限
4. 图片懒加载:先加载占位图,合适时机加载真实图片
查看答案即标记为已答
策略模式:定义一系列算法,将每个算法封装起来,使其可以互相替换。
示例 — 表单验证:
优点:避免大量 if/else,新增策略只需添加对象属性。
应用:表单验证、权限判断、折扣计算、动画缓动函数。
示例 — 表单验证:
const strategies = {
required: (value) => value.trim() ? '' : '必填',
minLength: (value, len) => value.length >= len ? '' : '最少' + len + '个字符',
email: (value) => /\S+@\S+\.\S+/.test(value) ? '' : '邮箱格式错误',
};
function validate(value, rules) {
for (const { type, param } of rules) {
const msg = strategies[type](value, param);
if (msg) return msg;
}
return '';
}优点:避免大量 if/else,新增策略只需添加对象属性。
应用:表单验证、权限判断、折扣计算、动画缓动函数。
查看答案即标记为已答
装饰器模式:动态给对象添加额外功能,不改变原对象。
JS 实现:
vs 继承:
• 继承:静态的,编译时确定,类层级可能很深
• 装饰器:动态的,运行时组合,更灵活
前端应用:
• React HOC(高阶组件)
• TypeScript 装饰器(@decorator)
• AOP 切面编程(日志、权限、缓存)
JS 实现:
function withLog(fn) {
return function(...args) {
console.log('调用参数:', args);
const result = fn.apply(this, args);
console.log('返回结果:', result);
return result;
};
}
const add = (a, b) => a + b;
const addWithLog = withLog(add);vs 继承:
• 继承:静态的,编译时确定,类层级可能很深
• 装饰器:动态的,运行时组合,更灵活
前端应用:
• React HOC(高阶组件)
• TypeScript 装饰器(@decorator)
• AOP 切面编程(日志、权限、缓存)
查看答案即标记为已答
中间件模式:将处理逻辑拆分为多个独立函数,按顺序执行,每个可以中断或继续。
Redux 中间件:
Koa 洋葱模型:
本质:函数式组合(compose),将
Redux 中间件:
const logger = store => next => action => {
console.log('dispatching', action);
const result = next(action); // 调用下一个中间件
console.log('next state', store.getState());
return result;
};Koa 洋葱模型:
app.use(async (ctx, next) => {
console.log('1 进'); // 请求进入
await next(); // 交给下一个中间件
console.log('1 出'); // 响应返回
});
app.use(async (ctx, next) => {
console.log('2 进');
await next();
console.log('2 出');
});
// 执行顺序:1 进 → 2 进 → 2 出 → 1 出本质:函数式组合(compose),将
[fn1, fn2, fn3] 组合为 fn1(fn2(fn3()))。 查看答案即标记为已答
工厂模式:将对象创建过程封装起来,调用者不需要知道具体实现。
简单工厂:
工厂方法:
优点:
1. 解耦 — 调用者不依赖具体类
2. 可扩展 — 新增类型只需修改工厂
3. 统一接口 — 所有产品遵循相同协议
简单工厂:
function createButton(type) {
const types = {
primary: { color: 'blue', text: '确认' },
danger: { color: 'red', text: '删除' },
default: { color: 'gray', text: '取消' },
};
return types[type];
}工厂方法:
class Dialog { createButton() {} }
class WindowsDialog extends Dialog { createButton() { return new WindowsButton(); } }
class WebDialog extends Dialog { createButton() { return new HTMLButton(); } }优点:
1. 解耦 — 调用者不依赖具体类
2. 可扩展 — 新增类型只需修改工厂
3. 统一接口 — 所有产品遵循相同协议
查看答案即标记为已答