# 定义
装饰器(Decorator)是一种特殊的语法,用于修改类、方法、属性或参数的行为。装饰器在很多语言中都有支持,JavaScript 在 ES6 中也引入了类装饰器,并在 TypeScript 中得到了广泛使用。装饰器语法可以增强类或其成员的功能,使代码更具可维护性和可扩展性。
装饰器目前属于【实验性语法】,因此在项目中使用可能需要用到对应插件进行解析、转换
# 基本语法
装饰器的基本形式是一个函数,它接收目标对象、属性名称等参数,并可以修改目标或属性的行为。
# 类装饰器
类装饰器是作用于整个类的,它接收类的构造函数作为参数,并可以对类进行修改或扩展。
function LogClass(target: Function) { | |
console.log(`Class decorated: ${target.name}`); | |
} | |
@LogClass | |
class MyClass { | |
constructor() { | |
console.log("MyClass instantiated"); | |
} | |
} | |
// 输出:Class decorated: MyClass |
# 方法装饰器
方法装饰器用于修改类中方法的行为。它接收三个参数:目标对象、属性名称以及方法的属性描述符。
function LogMethod( | |
target: any, | |
propertyKey: string, | |
descriptor: PropertyDescriptor | |
) { | |
const originalMethod = descriptor.value; | |
descriptor.value = function (...args: any[]) { | |
console.log(`Method ${propertyKey} called with args: ${args}`); | |
return originalMethod.apply(this, args); | |
}; | |
} | |
class MyClass { | |
@LogMethod | |
sayHello(name: string) { | |
console.log(`Hello, ${name}`); | |
} | |
} | |
const myClass = new MyClass(); | |
myClass.sayHello("John"); | |
// 输出:Method sayHello called with args: John | |
// 输出:Hello, John |
# 属性装饰器
属性装饰器用于修改类属性的行为,它接收目标对象和属性名称作为参数。
function LogProperty(target: any, propertyKey: string) { | |
let value = target[propertyKey]; | |
const getter = () => { | |
console.log(`Get value of ${propertyKey}: ${value}`); | |
return value; | |
}; | |
const setter = (newValue: any) => { | |
console.log(`Set value of ${propertyKey}: ${newValue}`); | |
value = newValue; | |
}; | |
Object.defineProperty(target, propertyKey, { | |
get: getter, | |
set: setter, | |
enumerable: true, | |
configurable: true, | |
}); | |
} | |
class MyClass { | |
@LogProperty | |
name: string; | |
constructor(name: string) { | |
this.name = name; | |
} | |
} | |
const myClass = new MyClass("Alice"); | |
// 输出:Set value of name: Alice | |
myClass.name = "Bob"; | |
// 输出:Set value of name: Bob | |
console.log(myClass.name); | |
// 输出:Get value of name: Bob | |
// 输出:Bob |
# 参数装饰器
参数装饰器用于修改方法参数的行为,接收目标对象、方法名称和参数索引作为参数。
function LogParameter( | |
target: any, | |
propertyKey: string, | |
parameterIndex: number | |
) { | |
console.log( | |
`Parameter ${parameterIndex} in method ${propertyKey} is being decorated` | |
); | |
} | |
class MyClass { | |
greet(@LogParameter name: string) { | |
console.log(`Hello, ${name}`); | |
} | |
} | |
// 输出:Parameter 0 in method greet is being decorated |
# 访问装饰器
访问器装饰器可以装饰类的 getter 和 setter,用法类似于方法装饰器。
function LogAccessor(target: any, propertyKey: string, descriptor: PropertyDescriptor) { | |
const originalGet = descriptor.get; | |
descriptor.get = function () { | |
console.log(`Getting value of ${propertyKey}`); | |
return originalGet?.apply(this); | |
}; | |
} | |
class MyClass { | |
private _value: string = "default"; | |
@LogAccessor | |
get value() { | |
return this._value; | |
} | |
set value(newValue: string) { | |
this._value = newValue; | |
} | |
} | |
const myClass = new MyClass(); | |
console.log(myClass.value); | |
// 输出:Getting value of value | |
// 输出:default |
# TperScript 装饰器配置
为了在 TypeScript 中使用装饰器,需要在 tsconfig.json 中启用装饰器相关配置:
{ | |
"compilerOptions": { | |
"experimentalDecorators": true, // 启用装饰器语法 | |
"emitDecoratorMetadata": true // 启用元数据反射 | |
} | |
} |
# 装饰器组合
多个装饰器可以叠加在一起使用,执行顺序为从下到上:
function First() { | |
console.log("First Decorator"); | |
return function (target: any) { | |
console.log("First decorator executed"); | |
}; | |
} | |
function Second() { | |
console.log("Second Decorator"); | |
return function (target: any) { | |
console.log("Second decorator executed"); | |
}; | |
} | |
@First() | |
@Second() | |
class MyClass {} |
# 实际应用场景
- 日志记录:可以通过装饰器自动记录类、方法的调用和参数。
- 权限检查:在执行方法前检查权限,或者验证用户是否有权限执行某些操作。
- 缓存:为某些方法结果添加缓存逻辑,减少不必要的重复计算。
- 依赖注入:在 Angular、NestJS 等框架中,装饰器常用于依赖注入,自动为类的构造函数注入依赖项。