TypeScript的装饰器
在一些场景下我们需要额外的特性来支持标注或修改类及其成员,如同设计模式中的装饰者模式,这就是Typescript中的装饰器。
定义和使用
装饰器是一种声明,能被附加到类,方法,访问符,属性或参数上。声明方式为@expression,expression是一个表达式,其求值后必须为一个函数,该函数会在运行时被调用,根据装饰器分类的不同,传入其中的参数也会不同。
装饰器工厂
我们已经知道了只要该表达式求值后为一个函数即可,那么我们可以通过一个工厂函数去决定一个装饰器如何应用到一个声明上。例子如下:
function color(value: string) { // 传入需要的参数
... // 自定义逻辑
return function (target) {
// 装饰器内部逻辑,可以利用闭包获取传入参数做更多的事
}
}
装饰器组合
多个装饰器通过以下两种书写方式应用到同一个声明上:
- 书写在同一行上
- 书写在多行上
当多个装饰器应用时,会按照如下步骤去执行装饰器:
- 由上至下对装饰器表达式求值
- 求值结果为装饰器函数,由下至上进行调用
装饰器分类
装饰器按照声明位置来区别,所有的装饰器都不能用在声明文件和任何外部上下文中。
类装饰器
类装饰器在类声明之前被声明,应用于类构造函数,可以用来监视,修改或替换类的定义。
类装饰器函数会被传入类的构造函数作为唯一参数。如果该函数返回了一个值,它会使用提供的构造函数替换掉类声明,但需要注意原型链不会有变化。
由于通过类装饰器我们可以得到类的构造函数,进而就可以对类的原型对象做一定修改,这也是比较常见的用法。例子如下:
function sealed(constructor: Function) {
Object.seal(constructor);
Ojbect.seal(constructor.prototype);
}
@sealed
class MyClass {
a: number = 0;
b: string = 'hello';
}
let obj = new MyClass();
obj.c = true; // 编译报错
方法装饰器
方法装饰器声明在一个方法声明之前,它会被应用到方法的属性描述符上,可以用来监视,修改或替换方法的定义。
方法装饰器函数会被传入3个参数:
- 对于静态成员传入类的构造函数,对于实例成员传入类的原型对象。
- 成员名字。
- 成员的属性描述符。
如果该函数返回了一个值,它会被用作方法的属性描述符。
需要注意的是装饰器方法只会在加载代码时调用一次,调用被装饰的方法时不会触发方法装饰器。例子如下:
function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}
// greet方法的属性描述符的enumerable的属性会被修改
访问器装饰器
访问器装饰器声明在一个访问器声明之前,它会被应用于访问器的属性描述符上,可以用来监视,修改或替换一个访问器的定义。不允许同时装饰一个成员的get和set访问器,一个成员的所有装饰都必须应用在按照文档顺序的第一个访问器上。
访问器装饰器传入参数和方法装饰器完全相同,用法也相同,参考方法装饰器即可。
属性装饰器
属性装饰器和访问器装饰器,方法装饰器也没有太大区别,唯一区别在于,属性装饰器函数不会传入第3个参数属性描述符,因为目前没有办法在定义一个原型对象成员时描述其实例属性。
参数装饰器
参数装饰器声明在一个参数声明之前,参数装饰器应用于类的构造函数或方法声明。
参数装饰器函数会被传入下列3个参数:
- 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象。
- 成员名字。
- 参数在函数参数列表中的索引。
该装饰器函数的返回值会被忽略。
不同装饰器调用顺序
类中如果声明了多个装饰器,将按以下顺序进行调用:
- 检查实例成员,依次调用参数装饰器,方法装饰器,访问器或属性装饰器。
- 检查静态成员,依次调用参数装饰器,方法装饰器,访问器或属性装饰器。
- 参数装饰器应用到构造函数。
- 类装饰器应用到类。
-- EOF --
前端开发
」下,并被添加
「TypeScript」
标签。