Decorator Pattern
装饰者模式的实现与应用
问题
什么是装饰者模式?如何在 JavaScript 中实现?
解答
装饰者模式允许在不修改原有对象的情况下,动态地给对象添加新功能。
基础实现
// 基础组件
class Coffee {
cost() {
return 10;
}
description() {
return '咖啡';
}
}
// 装饰器基类
class CoffeeDecorator {
constructor(coffee) {
this.coffee = coffee;
}
cost() {
return this.coffee.cost();
}
description() {
return this.coffee.description();
}
}
// 具体装饰器:加牛奶
class MilkDecorator extends CoffeeDecorator {
cost() {
return this.coffee.cost() + 3;
}
description() {
return this.coffee.description() + ' + 牛奶';
}
}
// 具体装饰器:加糖
class SugarDecorator extends CoffeeDecorator {
cost() {
return this.coffee.cost() + 1;
}
description() {
return this.coffee.description() + ' + 糖';
}
}
// 使用
let coffee = new Coffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
console.log(coffee.description()); // 咖啡 + 牛奶 + 糖
console.log(coffee.cost()); // 14
函数装饰器
// 装饰器函数:添加日志功能
function withLogging(fn) {
return function (...args) {
console.log(`调用 ${fn.name},参数:`, args);
const result = fn.apply(this, args);
console.log(`返回值:`, result);
return result;
};
}
// 装饰器函数:添加计时功能
function withTiming(fn) {
return function (...args) {
const start = performance.now();
const result = fn.apply(this, args);
const end = performance.now();
console.log(`${fn.name} 执行时间: ${end - start}ms`);
return result;
};
}
// 原始函数
function add(a, b) {
return a + b;
}
// 组合装饰
const decoratedAdd = withLogging(withTiming(add));
decoratedAdd(1, 2);
ES7 装饰器语法
// 方法装饰器
function log(target, name, descriptor) {
const original = descriptor.value;
descriptor.value = function (...args) {
console.log(`调用 ${name},参数:`, args);
return original.apply(this, args);
};
return descriptor;
}
// 类装饰器
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
class Calculator {
@log
add(a, b) {
return a + b;
}
@readonly
pi() {
return 3.14159;
}
}
const calc = new Calculator();
calc.add(1, 2); // 调用 add,参数: [1, 2]
实际应用:防抖装饰器
function debounce(delay) {
return function (target, name, descriptor) {
const original = descriptor.value;
let timer = null;
descriptor.value = function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
original.apply(this, args);
}, delay);
};
return descriptor;
};
}
class SearchBox {
@debounce(300)
search(keyword) {
console.log('搜索:', keyword);
}
}
关键点
- 装饰者模式在不修改原对象的前提下扩展功能,符合开闭原则
- 装饰器可以嵌套组合,灵活添加多个功能
- 函数式装饰器通过高阶函数实现,返回包装后的新函数
- ES7 装饰器是语法糖,本质是在定义时修改类或方法的描述符
- 常见应用:日志、缓存、权限校验、防抖节流
目录