JavaScript 设计模式
概述
设计模式是解决常见问题的可复用方案。
一、单例模式
javascript
class Singleton {
static instance = null;
static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
// 闭包实现
const createStore = (() => {
let instance;
return () => {
if (!instance) {
instance = { state: {} };
}
return instance;
};
})();二、观察者模式
javascript
class EventEmitter {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
return () => this.off(event, callback);
}
off(event, callback) {
if (!this.events[event]) return;
this.events[event] = this.events[event].filter(cb => cb !== callback);
}
emit(event, ...args) {
if (!this.events[event]) return;
this.events[event].forEach(cb => cb(...args));
}
once(event, callback) {
const wrapper = (...args) => {
callback(...args);
this.off(event, wrapper);
};
this.on(event, wrapper);
}
}三、发布-订阅模式
javascript
// 与观察者区别:有中间调度中心
class PubSub {
static channels = {};
static subscribe(channel, callback) {
if (!this.channels[channel]) {
this.channels[channel] = [];
}
this.channels[channel].push(callback);
}
static publish(channel, data) {
if (!this.channels[channel]) return;
this.channels[channel].forEach(cb => cb(data));
}
}
// 发布者和订阅者不直接通信
PubSub.subscribe('news', (data) => console.log(data));
PubSub.publish('news', { title: 'Hello' });四、策略模式
javascript
const strategies = {
A: (salary) => salary * 4,
B: (salary) => salary * 3,
C: (salary) => salary * 2
};
function calculateBonus(level, salary) {
return strategies[level](salary);
}
calculateBonus('A', 10000); // 40000五、代理模式
javascript
// 缓存代理
const mult = (...args) => args.reduce((a, b) => a * b);
const proxyMult = (() => {
const cache = {};
return (...args) => {
const key = args.join(',');
if (cache[key]) return cache[key];
return (cache[key] = mult(...args));
};
})();
// ES6 Proxy
const handler = {
get(target, key) {
console.log(`访问 ${key}`);
return target[key];
}
};
const proxy = new Proxy({ name: 'test' }, handler);六、装饰器模式
javascript
// 函数装饰器
function log(fn) {
return function (...args) {
console.log(`Call ${fn.name} with`, args);
return fn.apply(this, args);
};
}
const add = log((a, b) => a + b);
// ES7 装饰器 (需要 Babel)
function readonly(target, key, descriptor) {
descriptor.writable = false;
return descriptor;
}
class Example {
@readonly
name = 'test';
}七、工厂模式
javascript
class UserFactory {
static create(type) {
switch (type) {
case 'admin':
return new Admin();
case 'user':
return new User();
default:
throw new Error('Unknown type');
}
}
}面试高频题
Q1: 观察者 vs 发布-订阅?
观察者:主体直接通知观察者。发布-订阅:通过中间调度中心,解耦更彻底。
Q2: 前端常用设计模式?
单例 (Store)、观察者 (事件系统)、策略 (表单验证)、代理 (缓存)。
Q3: Vue 用了什么模式?
观察者 (响应式)、发布-订阅 (事件总线)、代理 (Proxy)。