装饰器
是 TypeScript 提供的一种强大功能,它允许你通过在类声明、方法、访问器、属性或参数前加上特殊 @expression 的形式,来修改或增强类和类成员的功能。装饰器本质上是一个函数,它接收类的元数据作为参数,并可以修改或返回一个新的类定义。装饰器在运行时被调用,可以在类实例化之前修改类的行为。基本语法
@decoratorName
class MyClass {
// ...
}类装饰器
类装饰器在类声明之前声明,可以修改类的定义。它接收类的构造函数作为参数,并可以返回一个新的构造函数或修改原来的构造函数。
类装饰器参数
类装饰器接收两个参数:
target:当前类的构造函数。context(可选,仅在 TypeScript 5.0+ 支持):提供有关装饰器调用上下文的信息,包括类的名称、修饰符等。
function logClass(target: Function) {
console.log(`Class Decorator: ${target.name} was decorated.`);
}
@logClass
class ExampleClass {}
// 输出: Class Decorator: ExampleClass was decorated.方法装饰器
方法装饰器在方法声明之前声明,可以修改方法的定义或行为。它接收三个参数:方法所在的类的原型对象,方法名,以及方法的描述符对象。
方法装饰器参数
方法装饰器接收三个参数:
target:- 对于实例成员,这是类的原型(
prototype)。 - 对于静态成员,这是类的构造函数。
- 对于实例成员,这是类的原型(
propertyKey:方法的名称,作为字符串或符号传递。descriptor:属性描述符,包含方法定义的详细信息。常见的属性包括:value:方法本身。configurable:是否可以删除或重新定义该属性。enumerable:是否可以在for...in循环中枚举该属性。writable:是否可以更改该属性的值。get:getter 方法。set:setter 方法。
在 TypeScript 5.0 及以上版本中,方法装饰器还可以接收一个可选的第四个参数 context,这个参数提供了关于装饰器调用上下文的额外信息。
function logMethod(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Method Decorator: Calling "${key}" with`, args);
return originalMethod.apply(this, args);
};
return descriptor;
}
class MyClass {
@logMethod
myMethod(arg1: string, arg2: number) {
console.log(`myMethod body: ${arg1}, ${arg2}`);
}
}
const instance = new MyClass();
instance.myMethod("Hello", 42);
// 输出:
// Method Decorator: Calling "myMethod" with ["Hello", 42]
// myMethod body: Hello, 42属性装饰器
属性装饰器在属性声明之前声明,可以修改或观察属性的定义。它接收两个参数:类的原型对象和属性名。
属性装饰器参数
属性装饰器接收两个参数:
target:- 对于实例成员,这是类的原型(
prototype)。 - 对于静态成员,这是类的构造函数。
- 对于实例成员,这是类的原型(
propertyKey:属性的名称,作为字符串或符号传递。
在 TypeScript 5.0 及以上版本中,属性装饰器还可以接收一个可选的第三个参数 descriptor,这个参数提供了关于属性定义的详细信息。
function logProperty(target: any, propertyName: string) {
console.log(`Property Decorator: Property "${propertyName}" was decorated.`);
}
class MyClass {
@logProperty
someProperty: string;
}
// 输出: Property Decorator: Property "someProperty" was decorated.参数装饰器
参数装饰器在方法的参数声明之前声明,可以访问或修改参数的信息。它接收三个参数:类的原型对象,方法名,以及参数在参数列表中的索引。
参数装饰器参数
参数装饰器接收三个参数:
target:- 对于实例成员,这是类的原型(
prototype)。 - 对于静态成员,这是类的构造函数。
- 对于构造函数参数,这也是类的构造函数。
- 对于实例成员,这是类的原型(
propertyKey:方法的名称或构造函数的名称,作为字符串或符号传递。如果装饰的是构造函数的参数,则为undefined。parameterIndex:参数在参数列表中的位置,作为数字传递。
在 TypeScript 5.0 及以上版本中,参数装饰器还可以接收一个可选的第四个参数 context,这个参数提供了关于装饰器调用上下文的额外信息。
function parameterDecorator(
target: any,
methodName: string,
paramIndex: number
) {
console.log(
`Parameter Decorator: Parameter at index ${paramIndex} of method "${methodName}" was decorated.`
);
}
class MyClass {
myMethod(@parameterDecorator arg1: string, arg2: number) {
// ...
}
}
const instance = new MyClass();
instance.myMethod("Hello", 42);
// 输出: Parameter Decorator: Parameter at index 0 of method "myMethod" was decorated.装饰器的使用需要在 TypeScript 编译时开启相应的实验性特性(如 --experimentalDecorators),并且在某些情况下还需要 --emitDecoratorMetadata 来支持反射元数据的生成。装饰器功能非常强大,但也需要谨慎使用,以避免过度复杂化代码结构或引入难以追踪的副作用。
使用场景
装饰器提供了一种灵活的元编程手段,使得开发者能够在不修改源代码的情况下,增强或改变类和类成员的功能,从而提高代码的可复用性、灵活性和可维护性。
- 日志记录和监控:可以在方法执行前后自动打印日志,或记录方法的执行时间,这对于性能监控和调试非常有用。
- 权限控制和验证:可以在方法调用前检查用户是否有权限执行该操作,或者验证传入参数的有效性。
- 注入依赖:实现依赖注入模式,自动为类或方法提供所需的依赖,这对于构建可测试和模块化的应用至关重要。
- 添加元数据:向类或其成员添加额外信息,这些信息可以用于自动生成文档、序列化、ORM 映射等。
- 修改类或方法的行为:可以在不修改原有代码的情况下,增加新的功能或改变现有功能的行为,比如添加缓存机制、事务管理等。
- 追踪和调试:帮助开发者理解代码的执行流程,尤其是在复杂的系统中,通过装饰器可以标记和记录关键路径。
- 类型检查和验证:在运行时检查属性或参数是否符合预期的类型,增强类型安全。
- 自动完成某些重复任务:比如自动为 RESTful API 的每个方法添加统一的错误处理、认证逻辑等。
- 性能优化:通过缓存装饰器减少重复计算,或者通过代理模式装饰器优化访问模式。
- 实现设计模式:如装饰器模式本身,可以用来动态地给对象添加责任,相比传统的继承更灵活。
装饰器工厂
装饰器工厂是一种特殊的装饰器,它接受一个参数并返回一个装饰器函数。装饰器工厂可以用来动态生成装饰器,根据不同的条件或需求来选择不同的装饰器行为。
function logClass(isDev: boolean) {
return function (target: Function) {
console.log(`Class Decorator: ${target.name} was decorated.`);
};
}
@logClass(true)
class ExampleClass {}
// 输出: Class Decorator: ExampleClass was decorated.装饰器工厂可以用来动态生成装饰器,根据不同的条件或需求来选择不同的装饰器行为。
装饰器组合
装饰器可以组合使用,以实现更复杂的逻辑。装饰器组合的顺序非常重要,因为它们会按照指定的顺序应用到目标对象上。
function logMethod(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Method Decorator: Calling "${key}" with`, args);
return originalMethod.apply(this, args);
};
return descriptor;
}
function logProperty(target: any, key: string) {
console.log(`Property Decorator: Property "${key}" was decorated.`);
}
class MyClass {
@logProperty
myProperty: string;
@logMethod
myMethod(arg1: string, arg2: number) {
// ...
}
}
// 输出:
// Property Decorator: Property "myProperty" was decorated.
// Method Decorator: Calling "myMethod" with "Hello", 42装饰器组合的顺序非常重要,因为它们会按照指定的顺序应用到目标对象上。
装饰器执行顺序
装饰器执行顺序按照以下规则进行:
- 参数装饰器
- 方法装饰器
- 访问符装饰器
- 属性装饰器
- 类装饰器
function logClass(isDev: boolean) {
return function (target: Function) {
console.log(`Class Decorator: ${target.name} was decorated.`);
};
}
function logMethod(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Method Decorator: Calling "${key}" with`, args);
return originalMethod.apply(this, args);
};
return descriptor;
}
function logProperty(target: any, key: string) {
console.log(`Property Decorator: Property "${key}" was decorated.`);
}
@logClass(true)
class MyClass {
@logProperty
myProperty: string;
@logMethod
myMethod(arg1: string, arg2: number) {
// ...
}
}
// 输出:
// Property Decorator: Property "myProperty" was decorated.
// Method Decorator: Calling "myMethod" with "Hello", 42
// Class Decorator: MyClass was decorated.