TypeScript装饰器
1. 前言
在介绍TypeScript装饰器之前,我们先来了解一下装饰器的概念。装饰器是一种特殊类型的声明,它可以附加到类声明、方法、属性或参数上,可以为它们添加额外的元数据或修改其行为。在JavaScript中,装饰器功能由ECMAScript提案提供,并在TypeScript中得到了支持。
本文将详细介绍TypeScript装饰器的使用方法和一些常见的应用场景。
2. 基本语法
在TypeScript中,装饰器由”@”符号紧跟在要修饰的内容之前。装饰器可以分为类装饰器、方法装饰器、属性装饰器和参数装饰器四种类型,它们分别用来修饰类声明、类方法、类属性和类方法的参数。
下面是装饰器的基本语法:
@装饰器名
class 类名 {
// 类定义
}
class 类名 {
@装饰器名
方法名() {
// 方法定义
}
}
class 类名 {
@装饰器名
属性名: 类型;
}
class 类名 {
方法名(@装饰器名 参数名: 类型) {
// 方法定义
}
}
3. 类装饰器
类装饰器用来修饰类声明,它接收一个参数,即要修饰的类的构造函数。类装饰器可以应用于普通类、抽象类和构造函数。
3.1 不带参数的类装饰器
先来看一个简单的示例,假设我们想在类声明之前打印一条日志:
function logClass(target: any) {
console.log("类装饰器被调用");
console.log(target);
}
@logClass
class MyClass {
// 类定义
}
上述代码定义了一个名为logClass
的类装饰器,它会在类被装饰时输出一条日志。使用该装饰器修饰了MyClass
类之后,当我们创建该类的实例时,会看到控制台输出以下内容:
类装饰器被调用
[Function: MyClass]
这说明类装饰器成功地被调用了。
3.2 带参数的类装饰器
类装饰器也可以接收参数,我们可以使用装饰器工厂函数来实现带参数的类装饰器。下面的示例演示了如何在类装饰器中接收一个字符串参数,并在装饰时输出该参数:
function logClass(message: string) {
return function(target: any) {
console.log("类装饰器被调用");
console.log("参数值为:" + message);
console.log(target);
}
}
@logClass("Hello World")
class MyClass {
// 类定义
}
当我们创建MyClass
类的实例时,控制台会输出以下内容:
类装饰器被调用
参数值为:Hello World
[Function: MyClass]
3.3 修改类的行为
类装饰器还可以用来修改类的行为。下面的示例展示了如何使用类装饰器来给类添加一个方法:
function addMethod(target: any) {
target.prototype.printMessage = function() {
console.log("Hello");
}
}
@addMethod
class MyClass {
// 类定义
}
const obj = new MyClass();
obj.printMessage(); // 输出 "Hello"
在上述示例中,addMethod
装饰器通过修改类的原型,向其添加了一个名为printMessage
的方法。当我们创建MyClass
类的实例并调用printMessage
方法时,会输出Hello
。
4. 方法装饰器
方法装饰器用来修饰类方法,它接收三个参数:装饰的目标对象、方法名和方法的属性描述符。
4.1 不带参数的方法装饰器
先来看一个不带参数的方法装饰器的示例,假设我们想在方法被调用时打印一条日志:
function logMethod(target: any, methodName: string, descriptor: PropertyDescriptor) {
console.log("方法装饰器被调用");
console.log("方法名:" + methodName);
console.log(target);
}
class MyClass {
@logMethod
myMethod() {
// 方法定义
}
}
在上述示例中,定义了一个名为logMethod
的方法装饰器,它会在修饰的方法被调用时输出方法名和目标类。当我们调用myMethod
方法时,控制台会输出以下内容:
方法装饰器被调用
方法名:myMethod
MyClass {}
4.2 带参数的方法装饰器
方法装饰器也可以接收参数,同样可以使用装饰器工厂函数来实现。下面的示例演示了如何在方法装饰器中接收一个字符串参数,并在装饰时输出该参数、方法名和目标类:
function logMethod(message: string) {
return function(target: any, methodName: string, descriptor: PropertyDescriptor) {
console.log("方法装饰器被调用");
console.log("方法名:" + methodName);
console.log("参数值为:" + message);
console.log(target);
}
}
class MyClass {
@logMethod("Hello World")
myMethod() {
// 方法定义
}
}
当我们调用myMethod
方法时,控制台会输出以下内容:
方法装饰器被调用
方法名:myMethod
参数值为:Hello World
MyClass {}
4.3 修改方法的行为
方法装饰器还可以用来修改方法的行为。下面的示例展示了如何使用方法装饰器来改变方法的返回值:
function modifyReturn(target: any, methodName: string, descriptor: PropertyDescriptor) {
const method = descriptor.value;
descriptor.value = function() {
const result = method.apply(this, arguments);
return result.toUpperCase();
}
}
class MyClass {
@modifyReturn
myMethod() {
return "hello";
}
}
const obj = new MyClass();
console.log(obj.myMethod()); // 输出 "HELLO"
在上述示例中,modifyReturn
装饰器通过修改方法的属性描述符,将方法的返回值转为大写后再返回。当我们调用myMethod
方法时,会输出HELLO
。
5. 属性装饰器
属性装饰器用来修饰类的属性,它接收两个参数:装饰的目标对象和属性名。
function logProperty(target: any, propertyName: string) {
console.log("属性装饰器被调用");
console.log("属性名:" + propertyName);
console.log(target);
}
class MyClass {
@logProperty
myProperty: string;
}
const obj = new MyClass();
obj.myProperty = "Hello";
console.log(obj.myProperty); // 输出 "Hello"
在上述例子中,定义了一个名为logProperty
的属性装饰器,它会在修饰的属性被赋值时输出属性名和目标类。当我们赋值给myProperty
属性时,并打印该属性的值,控制台会输出以下内容:
属性装饰器被调用
属性名:myProperty
MyClass {}
Hello
6. 参数装饰器
参数装饰器用来修饰类方法的参数,它接收三个参数:装饰的目标对象、方法名和参数在函数参数列表中的索引。
6.1 不带参数的参数装饰器
先来看一个不带参数的参数装饰器的例子,假设我们想在方法调用时打印每个参数的值:
function logParameter(target: any, methodName: string, parameterIndex: number) {
console.log("参数装饰器被调用");
console.log("方法名:" + methodName);
console.log("参数索引:" + parameterIndex);
console.log(target);
}
class MyClass {
myMethod(@logParameter param1: string, @logParameter param2: number) {
// 方法定义
}
}
const obj = new MyClass();
obj.myMethod("Hello", 123);
在上述例子中,定义了一个名为logParameter
的参数装饰器,它会在方法调用时输出方法名、参数索引和目标类。当我们调用myMethod
方法时,控制台会输出以下内容:
参数装饰器被调用
方法名:myMethod
参数索引:0
MyClass {}
参数装饰器被调用
方法名:myMethod
参数索引:1
MyClass {}
6.2 带参数的参数装饰器
参数装饰器也可以接收参数,使用装饰器工厂函数来实现。下面的例子演示了如何在参数装饰器中接收一个字符串参数,并在装饰时输出该参数、方法名和参数索引:
function logParameter(message: string) {
return function(target: any, methodName: string, parameterIndex: number) {
console.log("参数装饰器被调用");
console.log("方法名:" + methodName);
console.log("参数索引:" + parameterIndex);
console.log("参数值为:" + message);
console.log(target);
}
}
class MyClass {
myMethod(@logParameter("Hello World") param: string) {
// 方法定义
}
}
const obj = new MyClass();
obj.myMethod("Hello");
当我们调用myMethod
方法时,控制台会输出以下内容:
参数装饰器被调用
方法名:myMethod
参数索引:0
参数值为:Hello World
MyClass {}
7. 装饰器组合
我们可以将多个装饰器组合在一起使用,装饰器的执行顺序是从下到上、从右到左。
下面的例子展示了类装饰器、方法装饰器和属性装饰器的组合使用:
function logClass(target: any) {
console.log("类装饰器被调用");
}
function logMethod(target: any, methodName: string, descriptor: PropertyDescriptor) {
console.log("方法装饰器被调用");
}
function logProperty(target: any, propertyName: string) {
console.log("属性装饰器被调用");
}
@logClass
class MyClass {
@logProperty
myProperty: string;
@logMethod
myMethod() {
// 方法定义
}
}
当我们创建MyClass
类的实例时,控制台会按照装饰器的执行顺序输出以下内容:
属性装饰器被调用
方法装饰器被调用
类装饰器被调用
8. 总结
本文介绍了TypeScript装饰器的基本语法及其在类、方法、属性和参数修饰方面的应用。装饰器是一种强大的元编程特性,可以为代码添加元数据和修改行为,使我们能够更灵活地编写和组织代码。