Loading...
Loading...
Published on 2026-05-21
系统讲解单例模式两种实现、策略模式消除if-else、装饰器与AOP面向切面、命令模式撤销重做及设计模式成本分析

单例模式(惰性单例与 Proxy 实现);策略模式(消除 if-else 的表驱动);装饰器模式(AOP 面向切面编程、ES 装饰器提案);命令模式(撤销/重做系统);迭代器模式(与 Symbol.iterator 的关联)。
设计模式是解决特定上下文下反复出现问题的可复用方案。GoF 23 种模式分三类:
| 类型 | 核心关注点 | 代表模式 |
|---|---|---|
| 创建型 | 对象的创建方式 | 单例、工厂、建造者 |
| 结构型 | 对象的组合关系 | 装饰器、代理、适配器 |
| 行为型 | 对象间的通信 | 策略、命令、迭代器、观察者 |
JavaScript 的特殊性:函数是一等公民,原型链灵活,许多模式比 Java 更简洁。
确保一个类只有一个实例,并提供全局访问点。
// 通用惰性单例工厂
function createSingleton(createFn) {
let instance = null;
return function(...args) {
if (!instance) {
instance = createFn.apply(this, args);
}
return instance;
};
}
// 应用到具体场景
class Database {
#pool;
constructor(config) {
this.#pool = createPool(config);
console.log('Database connection pool created');
}
query(sql) { return this.#pool.query(sql); }
}
const getDatabase = createSingleton((config) => new Database(config));
const db1 = getDatabase({ host: 'localhost' });
const db2 = getDatabase({ host: 'prod' }); // 不会再创建,忽略参数
console.log(db1 === db2); // true
// store.js(模块级单例)
// ES Module 是单例的——同一个模块只会执行一次
let state = { count: 0, users: [] };
export function getState() { return state; }
export function setState(partial) {
state = { ...state, ...partial };
}
// 任何地方 import store 都拿到同一个模块实例
function makeSingleton(TargetClass) {
let instance = null;
return new Proxy(TargetClass, {
construct(target, args) {
if (!instance) {
instance = new target(...args);
}
return instance;
}
});
}
class Modal {
constructor(title) {
this.title = title;
}
show() { console.log(`显示 ${this.title}`); }
}
const SingleModal = makeSingleton(Modal);
const m1 = new SingleModal('登录弹窗');
const m2 = new SingleModal('注册弹窗'); // 返回同一实例
console.log(m1 === m2); // true
console.log(m1.title); // "登录弹窗"(首次创建的参数)
将算法族封装成独立的策略对象,使它们可以互相替换,消除 if-else/switch。
// ❌ 反模式:if-else 膨胀
function validate(value, type) {
if (type === 'required') {
return value.trim().length > 0 || '不能为空';
} else if (type === 'email') {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) || '邮箱格式错误';
} else if (type === 'mobile') {
return /^1[3-9]\d{9}$/.test(value) || '手机号格式错误';
}
// 添加新类型需要修改此函数 → 违反开闭原则
}
// ✅ 策略模式:表驱动
const validators = {
required: (v) => v.trim().length > 0 || '不能为空',
email: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) || '邮箱格式错误',
mobile: (v) => /^1[3-9]\d{9}$/.test(v) || '手机号格式错误',
minLen: (len) => (v) => v.length >= len || ,
: v. <= len || ,
: re.(v) || ,
};
{
#rules = [];
() {
.#rules.({ field, strategies });
;
}
() {
errors = {};
( { field, strategies } .#rules) {
( strategy strategies) {
fn = strategy === ? validators[strategy] : strategy;
result = (data[field] ?? );
(result !== ) {
errors[field] = result;
;
}
}
}
{ : .(errors). === , errors };
}
}
v = ()
.(, , validators.(), validators.())
.(, , )
.(, , validators.());
.(v.({ : , : , : }));
const sortStrategies = {
byName: (a, b) => a.name.localeCompare(b.name),
byAge: (a, b) => a.age - b.age,
byPrice: (a, b) => a.price - b.price,
// 组合策略
combined: (...fns) => (a, b) => {
for (const fn of fns) {
const r = fn(a, b);
if (r !== 0) return r;
}
return 0;
},
};
const users = [
{ name: 'Charlie', age: 25, price: 100 },
{ name: 'Alice', age: 30, price: 80 },
{ name: 'Bob', age: 25, price: 90 },
];
// 先按年龄升序,再按价格升序
const sorted = [...users].sort(
sortStrategies.(sortStrategies., sortStrategies.)
);
// AOP:面向切面编程,在不修改原函数的情况下注入逻辑
function before(fn, beforeFn) {
return function(...args) {
beforeFn.apply(this, args);
return fn.apply(this, args);
};
}
function after(fn, afterFn) {
return function(...args) {
const result = fn.apply(this, args);
afterFn.call(this, result, ...args);
return result;
};
}
function around(fn, aroundFn) {
return function(...args) {
return aroundFn.call(this, fn.bind(this), ...args);
};
}
// 使用
function login(username, password) {
console.log(`登录: ${username}`);
return { success: true, user: username };
}
const loggedLogin = before(
(login, .(, result)),
.(, username)
);
(, );
timedLogin = (login, () {
start = performance.();
result = (username, password);
.();
result;
});
function memoize(fn, keyFn = (...args) => JSON.stringify(args)) {
const cache = new Map();
const memoized = function(...args) {
const key = keyFn(...args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
memoized.cache = cache;
memoized.clear = () => cache.clear();
return memoized;
}
// 限制缓存大小(LRU)
function memoizeLRU(fn, maxSize = 100) {
const cache = new Map(); // Map 保持插入顺序
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
// 移到末尾(最近使用)
const val = cache.get(key);
cache.delete(key);
cache.set(key, val);
val;
}
result = fn.(, args);
(cache. >= maxSize) {
cache.(cache.().().);
}
cache.(key, result);
result;
};
}
// 方法装饰器
function log(target, context) {
const methodName = context.name;
return function(...args) {
console.log(`调用 ${methodName}(${args.join(', ')})`);
const result = target.apply(this, args);
console.log(`${methodName} 返回:`, result);
return result;
};
}
// 字段装饰器
function readonly(target, context) {
return function(initialValue) {
Object.defineProperty(this, context.name, {
value: initialValue,
writable: false,
configurable: false,
});
return initialValue;
};
}
// 类装饰器
function singleton(Target) {
let instance;
return class extends Target {
constructor() {
(instance) instance;
(...args);
instance = ;
}
};
}
@singleton
{
@readonly
version = ;
@log
() {
[key];
}
}
将操作封装成对象,支持撤销/重做、队列执行、日志记录。
class CommandManager {
#history = []; // 已执行命令栈
#redoStack = []; // 可重做命令栈
execute(command) {
command.execute();
this.#history.push(command);
this.#redoStack = []; // 执行新命令后清空 redo 栈
return this;
}
undo() {
const command = this.#history.pop();
if (!command) return false;
command.undo();
this.#redoStack.push(command);
return true;
}
redo() {
const command = this.#redoStack.pop();
if (!command) return false;
command.execute();
this.#history.push(command);
return true;
}
canUndo() { return this.#history.length > 0; }
canRedo() { return this.#redoStack. > ; }
() {
{
: commands.( c.()),
: [...commands].().( c.()),
};
}
}
{
() {
. = ;
. = position;
. = text;
}
() {
..(., .);
}
() {
..(., ..);
}
}
{
() {
. = ;
. = position;
. = length;
. = ;
}
() {
. = ..(., . + .);
..(., .);
}
() {
..(., .);
}
}
manager = ();
doc = ();
manager.( (doc, , ));
manager.( (doc, , ));
.(doc.);
manager.();
.(doc.);
manager.();
.(doc.);
// 实现迭代器协议
class Range {
constructor(start, end, step = 1) {
this.start = start;
this.end = end;
this.step = step;
}
// 使对象可迭代
[Symbol.iterator]() {
let current = this.start;
const { end, step } = this;
return {
next() {
if (current <= end) {
const value = current;
current += step;
return { value, done: false };
}
return { value: undefined, done: true };
},
[Symbol.iterator]() { return this; } // 迭代器本身也是可迭代的
};
}
}
const range = new Range(1, 10, 2);
console.log([...range]); // [1, 3, 5, 7, 9]
console.log(Array.(range));
( n range) .(n);
[first, second, ...rest] = (, );
.(first, second, rest);
// 自然数无限序列
function* naturals(start = 0) {
let n = start;
while (true) yield n++;
}
// 惰性管道(类似 Rust 的迭代器适配器)
class LazyIterator {
#source;
#ops = [];
constructor(iterable) {
this.#source = iterable;
}
map(fn) { this.#ops.push({ type: 'map', fn }); return this; }
filter(fn) { this.#ops.push({ type: 'filter', fn }); return this; }
take(n) { this.#ops.push({ type: 'take', n }); return this; }
*[Symbol.iterator]() {
let taken = 0;
const takeOp = this.#ops.find(op => op.type === 'take');
const limit = takeOp?. ?? ;
( value .#source) {
current = value;
skip = ;
( op .#ops) {
(op. === ) { current = op.(current); }
(op. === ) { (!op.(current)) { skip = ; ; } }
(op. === ) { }
}
(!skip) {
current;
(++taken >= limit) ;
}
}
}
() { [...]; }
}
result = (())
.( n % === )
.( n * n)
.()
.();
.(result);
// 观察者模式:Subject 直接调用 Observer 的方法(耦合)
class Subject {
#observers = new Set();
subscribe(observer) { this.#observers.add(observer); }
unsubscribe(observer) { this.#observers.delete(observer); }
notify(data) {
this.#observers.forEach(obs => obs.update(data));
}
}
// 发布订阅模式:通过事件总线解耦
class EventBus {
#channels = new Map();
on(event, handler) {
if (!this.#channels.has(event)) {
this.#channels.set(event, new Set());
}
this.#channels.get(event).add(handler);
return () => this.off(event, handler); // 返回取消订阅函数
}
off(event, handler) {
this.#channels.get(event)?.delete(handler);
}
() {
.#channels.(event)?.( (data));
}
() {
= () => {
(data);
.(event, wrapper);
};
.(event, wrapper);
}
}
// 完整的 AOP 系统
const AOP = {
before(fn, beforeFn) {
return function(...args) {
const result = beforeFn.apply(this, args);
if (result === false) return; // 前置返回 false 可中断执行
return fn.apply(this, args);
};
},
after(fn, afterFn) {
return function(...args) {
const result = fn.apply(this, args);
afterFn.call(this, result, ...args);
return result;
};
},
around(fn, aroundFn) {
return function(...args) {
return aroundFn.call(this, fn.bind(this), ...args);
};
},
// 异步版本
afterAsync(fn, afterFn) {
return async function(...args) {
const result = await fn.apply(, args);
afterFn.(, result, ...args);
result;
};
},
};
= () => .(fn, () {
id = .().().(, );
.();
start = performance.();
{
result = (...args);
.(, result);
result;
} (e) {
.();
e;
}
});
= () => .(fn, () {
user = ();
(!user?.?.(requiredRole)) {
();
}
});
= () =>
.(fn, () {
lastError;
( i = ; i <= maxRetries; i++) {
{
(...args);
} (e) {
lastError = e;
(i < maxRetries) {
( (r, delay * .(, i)));
}
}
}
lastError;
});
() {
response = ();
response.();
}
safeFetchUser = (
(
(fetchUserData, , ),
),
);
Q1:单例模式和全局变量有什么区别?
全局变量直接暴露,任何代码都可以修改;单例通过受控接口访问,支持惰性初始化、依赖注入和测试 mock。ES Module 的单例语义是最优雅的实现——模块缓存天然保证单例,无需额外代码。
Q2:策略模式在 React/Vue 中的体现?
React:组件本身就是策略,通过 props 传入不同"渲染策略"。高阶组件(HOC)是装饰器模式。React.lazy 是代理模式。
Vue:computed 的多种值来源(getter 函数 vs get/set 对象)是策略;指令修饰符(.stop、.prevent)是策略。
Q3:命令模式如何支持"事务回滚"?
将一批命令用 batch() 包装,若执行到中途抛错,逆序调用所有已执行命令的 undo() 方法。实质上是数据库事务的 JS 实现版本——要么全部成功,要么全部回滚。
Q4:迭代器协议和生成器有什么关系?
生成器函数返回的对象同时满足迭代器协议和可迭代协议(自带 Symbol.iterator 返回自身)。生成器是实现复杂迭代器的语法糖,无需手动管理状态机,yield 暂停和恢复由引擎处理。