装饰器(Decorator)是 TypeScript 中的一种特殊语法,用于修饰类、方法、属性或参数。装饰器本质上是一个函数,它可以在运行时修改或扩展被装饰对象的行为。装饰器通常用于实现横切关注点(如日志记录、权限检查、性能监控等),而不会侵入被装饰对象的代码。
装饰器的基本语法
装饰器使用 @
符号来应用,后面跟着一个装饰器函数。装饰器函数会在运行时被调用,并接收被装饰对象的元数据作为参数。
装饰器的类型
TypeScript 支持以下几种装饰器:
- 类装饰器:应用于类的构造函数。
- 方法装饰器:应用于类的方法。
- 属性装饰器:应用于类的属性。
- 参数装饰器:应用于类方法的参数。
- 访问器装饰器:应用于类的访问器(getter/setter)。
示例
1. 类装饰器
以下是一个简单的类装饰器示例,它会在类实例化时打印日志:
function logClass(target: Function) {
console.log(`Class ${target.name} is instantiated.`);
}
@logClass
class MyClass {
constructor() {
console.log("MyClass instance created.");
}
}
const instance = new MyClass();
// 输出:
// Class MyClass is instantiated.
// MyClass instance created.
在这个例子中,logClass
是一个类装饰器,它会在 MyClass
实例化时打印日志。
2. 方法装饰器
以下是一个方法装饰器示例,它会在方法调用时打印日志:
function logMethod(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Method ${key} is called with arguments: ${JSON.stringify(args)}`);
const result = originalMethod.apply(this, args);
console.log(`Method ${key} returned: ${result}`);
return result;
};
return descriptor;
}
class MyClass {
@logMethod
greet(name: string): string {
return `Hello, ${name}!`;
}
}
const instance = new MyClass();
instance.greet("Alice");
// 输出:
// Method greet is called with arguments: ["Alice"]
// Method greet returned: Hello, Alice!
在这个例子中,logMethod
是一个方法装饰器,它会在 greet
方法调用时打印日志。
3. 属性装饰器
以下是一个属性装饰器示例,它会在属性定义时打印日志:
function logProperty(target: any, key: string) {
let value = target[key];
const getter = function() {
console.log(`Getting value of property ${key}: ${value}`);
return value;
};
const setter = function(newVal: any) {
console.log(`Setting value of property ${key} to: ${newVal}`);
value = newVal;
};
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
class MyClass {
@logProperty
name: string;
constructor(name: string) {
this.name = name;
}
}
const instance = new MyClass("Alice");
instance.name = "Bob";
console.log(instance.name);
// 输出:
// Setting value of property name to: Alice
// Setting value of property name to: Bob
// Getting value of property name: Bob
// Bob
在这个例子中,logProperty
是一个属性装饰器,它会在 name
属性被访问或修改时打印日志。
4. 参数装饰器
以下是一个参数装饰器示例,它会在方法参数定义时打印日志:
function logParameter(target: any, key: string, parameterIndex: number) {
console.log(`Parameter ${parameterIndex} of method ${key} is decorated.`);
}
class MyClass {
greet(@logParameter name: string): string {
return `Hello, ${name}!`;
}
}
const instance = new MyClass();
instance.greet("Alice");
// 输出:
// Parameter 0 of method greet is decorated.
在这个例子中,logParameter
是一个参数装饰器,它会在 greet
方法的参数定义时打印日志。
5. 访问器装饰器
以下是一个访问器装饰器示例,它会在访问器被调用时打印日志:
function logAccessor(target: any, key: string, descriptor: PropertyDescriptor) {
const originalGet = descriptor.get;
const originalSet = descriptor.set;
if (originalGet) {
descriptor.get = function() {
console.log(`Getting value of accessor ${key}`);
return originalGet.apply(this);
};
}
if (originalSet) {
descriptor.set = function(value: any) {
console.log(`Setting value of accessor ${key} to: ${value}`);
originalSet.apply(this, [value]);
};
}
return descriptor;
}
class MyClass {
private _name: string;
@logAccessor
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
}
const instance = new MyClass();
instance.name = "Alice";
console.log(instance.name);
// 输出:
// Setting value of accessor name to: Alice
// Getting value of accessor name
// Alice
在这个例子中,logAccessor
是一个访问器装饰器,它会在 name
访问器被调用时打印日志。
装饰器的执行顺序
当多个装饰器应用于同一个目标时,它们的执行顺序如下:
- 参数装饰器:按照参数定义的顺序执行。
- 方法装饰器:按照方法定义的顺序执行。
- 访问器装饰器:按照访问器定义的顺序执行。
- 属性装饰器:按照属性定义的顺序执行。
- 类装饰器:按照类定义的顺序执行。
总结
装饰器是 TypeScript 中用于修饰类、方法、属性或参数的特殊语法。通过装饰器,你可以在运行时修改或扩展被装饰对象的行为,而不会侵入被装饰对象的代码。装饰器通常用于实现横切关注点,如日志记录、权限检查、性能监控等。理解装饰器的使用场景和语法,可以帮助你编写出更灵活、更可维护的代码。
