js怎么实现装饰器
装饰器基础概念
装饰器是一种特殊函数,用于修改类、方法或属性的行为。在JavaScript中,装饰器通过@符号调用,目前处于提案阶段(Stage 3),需通过Babel或TypeScript编译支持。
环境配置
使用装饰器需配置Babel或TypeScript:
- Babel:安装
@babel/plugin-proposal-decorators插件,配置.babelrc:{ "plugins": [ ["@babel/plugin-proposal-decorators", { "legacy": true }] ] } - TypeScript:在
tsconfig.json中启用:{ "compilerOptions": { "experimentalDecorators": true } }
类装饰器
类装饰器接收目标类构造函数,可扩展或修改类:
function logClass(target) {
console.log(`Class ${target.name} decorated`);
// 添加原型方法
target.prototype.print = () => console.log('Decorated method');
}
@logClass
class MyClass {}
const instance = new MyClass();
instance.print(); // 输出: "Decorated method"
方法装饰器
方法装饰器接收三个参数:目标对象、方法名、属性描述符。常用于拦截方法调用:
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
class User {
@readonly
getName() { return 'John'; }
}
const user = new User();
user.getName = () => 'Hack'; // 报错:无法重写只读方法
属性装饰器
属性装饰器接收目标对象和属性名,常用于元数据标记:
function format(formatString) {
return function (target, key) {
Reflect.defineMetadata('format', formatString, target, key);
};
}
class Book {
@format('YYYY-MM-DD')
publishDate = '2023-01-01';
}
// 通过反射获取元数据
const metadata = Reflect.getMetadata('format', Book.prototype, 'publishDate');
console.log(metadata); // 输出: "YYYY-MM-DD"
参数装饰器
参数装饰器用于标记方法参数,接收目标对象、方法名和参数索引:
function validate(type) {
return function (target, key, index) {
Reflect.defineMetadata('validate', { type, index }, target, key);
};
}
class ApiService {
fetch(@validate('number') id) {
return { data: id };
}
}
装饰器工厂
通过高阶函数实现可配置装饰器:
function delay(ms) {
return function (target, key, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
setTimeout(() => originalMethod.apply(this, args), ms);
};
return descriptor;
};
}
class Logger {
@delay(1000)
log(message) {
console.log(message);
}
}
const logger = new Logger();
logger.log('Delayed message'); // 1秒后输出
注意事项
- 装饰器在类定义时执行,而非实例化时。
- 装饰器不能用于函数(因存在函数提升)。
- 第三方库如
core-decorators提供常用装饰器(如@deprecated)。
通过组合不同类型的装饰器,可实现AOP(面向切面编程)、日志、权限控制等复杂功能。







