记录一下学习装饰器的过程。
参考文档 https://www.typescriptlang.org/docs/handbook/decorators.html
参考博客 https://saul-mirone.github.io/zh-hans/a-complete-guide-to-typescript-decorator/
先讲要点
装饰器包括:
- 类装饰器
- 类方法装饰器
- 类属性装饰器
- 类参数装饰器
- 类访问器装饰器
功能:
- 增加与修改类成员。
- 为类方法扩展新功能
- 通过和
reflect-metadata
库一起使用,可以在对类实例化之前获得成员类型。
- 通过和
reflect-metadata
库一起使用,实现依赖注入。
类装饰器
- 类装饰器的参数是被装饰的类,返回值为被装饰的类,我们可以通过继承的方式,为被装饰的类增加一些方法和属性。
- 要注意,如果要增加新的方法或属性,需要在被装饰类中提前声明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
function ClassDecorator<T extends { new (...args: any[]): {} }>( constructor: T ) { return class extends constructor { newVal = 100; print() { console.log(this.newVal); } }; }
function ClassDecoratorFactory(variable: any) { return ClassDecorator; }
@ClassDecorator class A { newVal!: number; val = 10;
print!: () => void; }
const a = new A(); a.print();
|
方法装饰器
方法装饰器,参数
- target: 对于静态成员来说是类的构造器,对于实例成员来说是类的原型链。
- propertyKey: 属性的名称。
- descriptor: 属性的描述器。
方法装饰器通常用来为被装饰的方法提供额外的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
function LoggerDecorator( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { const oldMethod: Function = descriptor.value;
descriptor.value = function () { console.log("logger", "start"); oldMethod.apply(this, arguments); console.log("loager", "end"); }; }
class A { @LoggerDecorator exec() { console.log("exec"); } }
const a = new A(); a.exec();
|
访问器装饰器
三个参数参照方法装饰器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
function Suffix(value: string) { return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { const getter = descriptor.get; if (getter) { descriptor.get = function () { return getter.call(this) + value; }; } }; }
class A { _width = 100;
@Suffix('px'); get width() { return this._width; } }
const a = new A; console.log(a.width);
|
参数装饰器
参数装饰器需要借助reflect-metadata
这个库。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
import "reflect-metadata";
const bodyMetaDataKey = Symbol("body");
function body( target: Object, propertyKey: string | symbol, parameterIndex: number ) { const argumentsMap = Reflect.getOwnMetadata(bodyMetaDataKey, target, propertyKey) || {};
argumentsMap[parameterIndex] = document.body; Reflect.defineMetadata(bodyMetaDataKey, argumentsMap, target, propertyKey); }
function bodyDecorator( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { let method = descriptor.value;
descriptor.value = function () { let argumentsMap: Record<string, any> = Reflect.getMetadata( bodyMetaDataKey, target, propertyKey );
const argvs = [...arguments]; Object.keys(argumentsMap).forEach((key) => { argvs[Number(key)] = argumentsMap[key]; });
return method.apply(this, argvs); }; }
class A { @bodyDecorator run(@body body: HTMLElement) { console.log(body); } }
const a = new A(); a.run(document.createElement('div'));
|
属性装饰器
可以借助reflect-metadata
收集属性的类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import 'reflect-metadata';
function Property(target: any, propertyKey: string) { const type = Reflect.getMetadata('design:type', target, propertyKey); console.log(type); }
class A { @Property name!: string; }
|