首先我们来说一下什么是装饰器,其实TS中的装饰器和很多的静态语言(Java,C#)等中的装饰器其实是一样的,它会为被装饰的内容添加一些特定的内容,而装饰器本身就是一个方法,它需要遵循两个条件: 1. 不能够修改被装饰内容的源代码 2. 不能够修改被装饰内容的调用
那么,我们进来就来说说在Ts中如何使用装饰器:
(注:在TS中使用装饰器之前我们需要现在tsconfig.json这个配置文件中配置"experimentalDecorators" 和 "emitDecoratorMetadata"这两个选项为true)
- 在类中使用装饰器:
首先我们定义一个Person类:
class Person {
constructor(public name: string) {}
}
此时我们使用装饰器为这个类添加一个age的属性:
// 这里我们使用了泛型定义了这个函数的参数类型: <T extends new (...args: any[]) => {}>
// 这个泛型T继承的内容,我们可以将它看做是一个类的类型,我们定义的类装饰器函数接收到的是一个类的构造函数
function addAgeDecorator<T extends new (...args: any[]) => {}>(constructor: T) {
// 装饰器函数返回一个新的类,并且这个类继承自它所装饰的那个类, 在这个返回的类中我们加上了一个age属性
return class extends constructor {
age = 18;
}
}
这时我们即可以这样去使用:
@addAgeDecorator
class Person {
constructor(public name: string) {}
}
这里再做一些说明:
- 装饰器在调用时使用@符号
- 装饰器的执行时机是在类的定义完成后立即被执行,完成对类的装饰
- 装饰器的这个函数中拿到的是类的构造函数,能够完成对类的一些修改
- 一个类可以被多个装饰器所装饰,当存在多个装饰器时,装饰器的执行顺序是从下到上的
- 如果我们想要在装饰器中进行一定的逻辑处理的话,推荐使用工厂模式对装饰器函数进行封装,例如我们可以这样书写上面的那个装饰器:
function addAgeDecorator() {
return function <T extends new (...args: any[]) => {}>(constructor: T) {
return class extends constructor {
age = 20;
}
}
}
- 在方法中使用装饰器:
有如下一个类:
class Person {
constructor(public _name: string) {}
get name(){
return this._name
}
}
我们使用一个函数装饰器来装饰这个getter
// 在方法装饰器中我们可以接收到两个参数:
// target: target表示的是当前方法所在类的一个显式原型:prototype
// key: key参数表示的是当前所装饰方法的名字
function getNameDecorator(target: any, key: string) {
console.log(target, key);
}
此时即可以使用@getNameDecorator去装饰getter方法了
关于方法装饰器的几点补充:
- 方法装饰器可以接收到所装饰方法所在类的prototype和方法名字两个参数
- 当所装饰的是一个类的静态方法时,还可以接收到第三个参数: descriptor: PropertyDescriptor;这是一个控制器,可以直接对所装饰的方法进行控制
- 不能同时对getter和setter使用相同的装饰器
- 属性的装饰器:
(由于属性装饰器的使用和上面相似,所以这里就不在定义相应的类了)
function nameDecorator(target: any, key: string): any {
const descriptor: PropertyDescriptor = {
writable: false
}
return descriptor;
}
以上我们便定义了一个属性装饰器,它的定义和方法装饰器相似,但是有几点区别:
- 属性装饰器接收到的参数仍然有两个: target仍然是当前对象的prototype, key是所装饰的属性的名称
- 在属性装饰器中不存在descriptor,但是我们可以在属性装饰器中创建一个descriptor去替换原有属性的descriptor,如上面的例子,我们即在属性装饰器中创建了一个descriptor
- 我们在属性装饰器中使用target[key]的形式修改属性的值修改的是类prototype上的值,并不是修改的属性的真实的值,而我们直接去修改属性实例上的值是无法完成的,大家可以根据装饰器的调用时机进行理解
- 参数装饰器:
function paramDecorator(target: any, method: string, paramIndex: number): any {
console.log(target, method, paramIndex)
}
如上所示,参数装饰器的定义和前两种装饰器的定义也是相似的,参数装饰器装饰的是我们传递的方法的参数,使用时也是直接使用@符号即可
参数装饰器中的参数:
- 参数装饰器的第一个参数仍然是当前类的原型
- 参数装饰器的第二个参数是参数所在的方法名
- 参数装饰器的第三个参数是参数所在方法中的位置,及为第一个参数是则为0,以此类推