设计模式篇
工厂模式
原理:通过封装对象创建逻辑,将实例化过程与使用分离,由工厂类/方法控制具体对象的生成。
特性
封装性:隐藏对象创建细节,暴露统一接口
子类决策:工厂方法由子类决定实例化哪个类(工厂方法模式)
扩展性:新增产品只需扩展工厂,无需修改原有代码
优点:
✅ 解耦对象创建与业务逻辑
✅ 符合开闭原则(扩展方便)
✅ 统一管理对象
缺点:
❌ 增加类的数量(工厂+产品类)
❌ 简单工厂违反单一职责原则
// 产品接口
class Button { render() {} }
class Input { render() {} }
// 工厂类
class ComponentFactory {
createComponent(type) {
return type === 'button' ? new Button() : new Input();
}
}
// 使用
const factory = new ComponentFactory();
const button = factory.createComponent('button');
单例模式
原理:确保类仅有一个实例,并提供全局访问点控制实例化过程
特性:
唯一实例:全局唯一且可共享
惰性初始化:首次使用时创建实例
私有构造:禁止外部直接实例化
优点:
✅ 控制资源实例数量(如数据库连接池)
✅ 全局状态管理(配置对象/日志记录器)
✅ 避免重复创建开销
缺点:
❌ 隐藏类间依赖关系
❌ 单元测试困难(全局状态污染)
❌ 多线程需额外同步处理(JS无此问题)
class Singleton {
constructor() {
if (Singleton.instance) return Singleton.instance;
this.data = "唯一实例";
Singleton.instance = this;
}
static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true
代理模式
原理:通过代理对象控制对真实对象的访问,可在访问前后添加额外逻辑(如权限校验、懒加载)。
特性:
控制访问:代理拦截主体对象的操作
延迟加载:按需创建高成本对象(如图片懒加载)
透明扩展:客户端无需感知代理存在
优点:
✅ 解耦主体与附加逻辑(如权限/缓存)
✅ 提升性能(延迟初始化高耗时对象)
✅ 灵活扩展功能(不影响原始对象)
缺点:
❌ 增加系统复杂度
❌ 内存占用(需维护代理对象)
❌ 调试困难(虚实对象分离)
// 图片懒加载代理
class Image {
constructor(url) {
this.url = url;
this.loadImage();
}
loadImage() {
console.log(`Loading image: ${this.url}`);
}
}
const ProxyImage = new Proxy(Image, {
construct(target, args) {
const img = new target(...args);
img.loadImage = () => console.log(`Lazy load: ${img.url}`);
return img;
}
});
const avatar = new ProxyImage("avatar.jpg"); // 未立即加载
document.onclick = () => avatar.loadImage(); // 点击后触发加载
观察者模式
原理:定义对象间一对多依赖关系,当被观察对象状态变化时,自动通知所有依赖者(订阅者)
特性:
松耦合:观察者与被观察者通过抽象接口交互
广播通信:一对多触发更新(如DOM事件冒泡)
动态绑定:运行时可增删观察者
优点:
✅ 解耦数据源与数据处理逻辑
✅ 支持广播式通信(如UI事件系统)
✅ 符合开闭原则(新增观察者不影响主题)
缺点:
❌ 可能引发内存泄漏(未及时移除观察者)
❌ 过度使用导致性能问题(频繁广播)
❌ 观察者执行顺序不确定
// 发布-订阅实现
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
update(data) {
console.log(`Received data: ${data}`);
}
}
const subject = new Subject();
const observer1 = new Observer();
subject.addObserver(observer1);
subject.notify("Hello World"); // 输出: Received data: Hello World
发布订阅模式
原理:通过事件中心(Event Bus)解耦发布者与订阅者,双方不直接依赖,消息以广播形式传递
特性:
中间层隔离:事件中心管理订阅与发布关系
多对多通信:一个事件可被多个订阅者监听,一个订阅者可监听多个事件
命名空间:通过事件名称字符串精准控制消息流向
优点:
✅ 彻底解耦生产者和消费者
✅ 支持异步消息队列处理
✅ 灵活组合事件流(如事件优先级/过滤)
缺点:
❌ 调试困难(难以追踪事件传播路径)
❌ 过度使用导致事件风暴
❌ 内存泄漏风险(未及时注销监听器)
// 事件中心实现
class EventBus {
constructor() {
this.events = {};
}
on(event, callback) {
(this.events[event] || (this.events[event] = [])).push(callback);
}
emit(event, ...args) {
this.events[event]?.forEach(cb => cb(...args));
}
off(event, callback) {
this.events[event] = this.events[event]?.filter(cb => cb !== callback);
}
}
// 使用
const bus = new EventBus();
bus.on('userLogin', (user) => console.log(`Welcome ${user}`));
bus.emit('userLogin', 'Alice'); // 输出: Welcome Alice
模块化模式
原理:将代码拆分为独立功能单元,通过封装隐藏实现细节,暴露公共接口实现低耦合交互
特性:
封装性:内部状态私有化,仅暴露必要方法
依赖隔离:模块间通过显式接口通信
按需加载:支持动态加载/卸载模块
作用域隔离:避免全局变量污染
优点:
✅ 提升代码可维护性与复用性
✅ 隔离副作用(如UI组件独立更新)
✅ 支持并行开发(团队协作友好)
缺点:
❌ 需要构建工具支持(打包/依赖管理)
❌ 过度拆分增加通信复杂度
❌ 动态加载可能影响首屏性能
// IIFE模块(ES5)
const Calculator = (() => {
let result = 0;
return {
add: (num) => result += num,
getResult: () => result
};
})();
Calculator.add(5);
console.log(Calculator.getResult()); // 5
// ES6模块(现代语法)
export class Logger {
static info(msg) { console.log(`[INFO] ${msg}`); }
}
// 使用
import { Logger } from './logger.js';
Logger.info('Module loaded');
构造器模式
原理:通过定义构造函数规范对象初始化流程,由new操作符触发实例化,每个实例拥有独立属性
特性:
原型继承:可通过prototype共享方法
参数化初始化:构造函数接收参数定制实例
强类型标识:instanceof可识别对象类型
优点:
✅ 直观定义对象结构(符合直觉的OOP)
✅ 实例属性独立(避免共享引用问题)
✅ 支持原型链优化内存
缺点:
❌ 无法动态选择子类类型(需结合工厂模式)
❌ 构造函数参数过多时可读性差
❌ 不支持静态方法直接关联
// 构造函数定义
class Car {
constructor(make, model) {
this.make = make;
this.model = model;
}
getInfo() {
return `${this.make} ${this.model}`;
}
}
// 实例化
const car1 = new Car('Tesla', 'Model S');
const car2 = new Car('Ford', 'Mustang');
console.log(car1.getInfo()); // Tesla Model S
console.log(car1 instanceof Car); // true
原型模式
原理:通过克隆现有对象创建新实例,避免重复初始化,利用原型链机制实现对象复用
特性:
对象克隆:基于原型对象创建新实例(Object.create() / 构造函数 prototype)
共享方法:原型上的方法被所有实例共享
动态扩展:运行时修改原型可影响所有后代实例
优点:
✅ 减少重复构造开销(如复杂对象初始化)
✅ 动态更新对象行为(修改原型即生效)
✅ 支持按需克隆(浅拷贝/深拷贝灵活控制)
缺点:
❌ 深拷贝实现复杂(需递归复制嵌套对象)
❌ 引用类型属性共享(如数组/对象属性需特殊处理)
❌ 对象修改可能污染原型链
// 原型对象
const carPrototype = {
init(make, model) {
this.make = make;
this.model = model;
return this;
},
getInfo() {
return `${this.make} ${this.model}`;
}
};
// 克隆创建实例
const car1 = Object.create(carPrototype).init('Tesla', 'Model S');
const car2 = Object.create(carPrototype).init('Ford', 'Mustang');
console.log(car1.getInfo()); // Tesla Model S
console.log(car1.__proto__ === car2.__proto__); // true
// ES6类实现
class Car {
constructor(make, model) {
this.make = make;
this.model = model;
}
}
Car.prototype.getInfo = function() {
return `${this.make} ${this.model}`;
};
const car3 = new Car('Toyota', 'Corolla');
混入模式(Mixin模式)
原理:通过将多个对象的属性和方法合并到目标对象中,实现功能复用,突破单继承限制,模拟多继承效果
特性:
多继承模拟:横向组合多个来源的功能
动态注入:运行时按需混入属性/方法
无侵入性:不修改原有类结构
优点:
✅ 代码复用率最大化(避免重复定义)
✅ 灵活组合功能(如混搭UI组件特性)
✅ 兼容性强(适配旧版JS/TS环境)
缺点:
❌ 命名冲突风险(同名方法覆盖问题)
❌ 原型链污染(慎用Object.assign全局混入)
❌ 调试困难(方法来源难以追溯)
const canEat = {
eat: function() { console.log(`${this.name} is eating`); }
};
const canFly = {
fly: function() { console.log(`${this.name} is flying`); }
};
class Bird {
constructor(name) { this.name = name; }
}
Object.assign(Bird.prototype, canEat, canFly);
const sparrow = new Bird('Sparrow');
sparrow.eat(); // Sparrow is eating
sparrow.fly(); // Sparrow is flying