装饰器模式是一种结构型设计模式,它允许你通过将对象放入包含行为的特殊封装对象中来为原对象动态添加新的行为。在 JavaScript 中,我们可以使用函数转发和 call/apply 方法来实现这种模式。
装饰器模式的核心思想是:不修改原有对象,而是通过包装(装饰)来扩展功能。
function doSomething() {
console.log('Doing something');
}
// 装饰器函数
function decorator(func) {
return function() {
console.log('Before calling the original function');
const result = func.apply(this, arguments);
console.log('After calling the original function');
return result;
};
}
const decoratedDoSomething = decorator(doSomething);
decoratedDoSomething();
call
和 apply
是函数对象的方法,用于调用函数并指定 this
值:
function greet() {
console.log(`Hello, ${this.name}`);
}
const person = { name: 'Alice' };
// 使用 call
greet.call(person); // Hello, Alice
// 使用 apply
greet.apply(person); // Hello, Alice
区别在于参数传递方式:
call
接受参数列表apply
接受参数数组function slow(x) {
console.log(`Called with ${x}`);
return x;
}
function cachingDecorator(func) {
const cache = new Map();
return function(x) {
if (cache.has(x)) {
return cache.get(x);
}
const result = func.call(this, x);
cache.set(x, result);
return result;
};
}
slow = cachingDecorator(slow);
console.log(slow(1)); // 计算并缓存
console.log(slow(1)); // 从缓存获取
let worker = {
someMethod() {
return 1;
},
slow(x) {
console.log(`Called with ${x}`);
return x * this.someMethod();
}
};
function cachingDecorator(func) {
const cache = new Map();
return function(x) {
if (cache.has(x)) {
return cache.get(x);
}
const result = func.call(this, x); // 保持正确的 this
cache.set(x, result);
return result;
};
}
worker.slow = cachingDecorator(worker.slow);
console.log(worker.slow(2)); // 正常工作
function hash() {
return Array.from(arguments).join(',');
}
function cachingDecorator(func) {
const cache = new Map();
return function() {
const key = hash(...arguments);
if (cache.has(key)) {
return cache.get(key);
}
const result = func.apply(this, arguments);
cache.set(key, result);
return result;
};
}
ES6+ 引入了类装饰器语法(目前是 stage 3 提案):
@decorator
class MyClass {}
// 或
class MyClass {
@decorator
method() {}
}
实现示例:
function log(target, name, descriptor) {
const original = descriptor.value;
descriptor.value = function(...args) {
console.log(`Calling ${name} with`, args);
const result = original.apply(this, args);
console.log(`Result: ${result}`);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a, b) {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3); // 日志将被打印
call
和 apply
是实现装饰器的关键工具,用于正确转发调用装饰器模式是 JavaScript 中非常强大的技术,特别适合实现 AOP(面向切面编程)风格的代码组织。