装饰器概念
装饰器-Decorators
在 TypeScript
中是一种可以在不修改类代码的基础上通过添加标注的方式来对类型进行扩展的一种方式
在 TypeScript
中,装饰器只能在类中使用
装饰器的使用
使用装饰器需要配置文件中启用experimentalDecorators: true
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
| function log(target: Function, name: string, descriptor: PropertyDescriptor) {
let fn = descriptor.value descriptor.value = function (a: number, b: number) { const result = fn(a, b) console.log(`${a}+${b}=${result}`) return result } }
class M { @log static add(a: number, b: number) { return a + b } }
let v1 = M.add(1, 2) console.log(v1)
|
装饰器
装饰器
是一个函数,它可以通过 @装饰器函数
这种特殊的语法附加在 类
、方法
、访问符
、属性
、参数
上,对它们进行包装,然后返回一个包装后的目标对象(类
、方法
、访问符
、属性
、参数
),装饰器工作在类的构建阶段,而不是使用阶段
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
| function 装饰器1() {} ...
@装饰器1 class MyClass {
@装饰器2 a: number;
@装饰器3 static property1: number;
@装饰器4 get b() { return 1; }
@装饰器5 static get c() { return 2; }
@装饰器6 public method1(@装饰器5 x: number) { }
@装饰器7 public static method2() {} }
|
装饰器的参数
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
| function d1(target: Function) { console.log(typeof target, target); } function d11(target: Function) { console.log(typeof target, target, '1111111'); }
function d2(target: any, name: string) { console.log(typeof target, name); } function d3(target: any, name: string, descriptor: PropertyDescriptor) { console.log(typeof target, name, descriptor); } function d4(target: any, name: string, descriptor: PropertyDescriptor) { console.log(typeof target, name, descriptor); } function d5(target: any, name: string, index: number) { console.log(typeof target, name, index); }
@d1 @d11 class MyClass {
@d2 static property1: number;
@d2 a: number;
@d3 get b() { return 1; }
@d3 static get c() { return 2; }
@d4 public method1(@d5 x: number, @d5 y: number) {}
@d4 public static method2() {}
}
|
装饰器 | 目标 | 第一个参数 | 第二个参数 | 第三个参数 |
---|
类装饰器 | 应用于类的构造函数 | 类的构造函数作为其唯一的参数 | × | × |
方法装饰器 | 应用于类的方法上 | 静态方法:类的构造函数 实例方法:类的原型对象 | 方法名称 | 方法描述符对象 |
属性装饰器 | 应用于类的属性上 | 静态方法:类的构造函数 实例方法:类的原型对象 | 属性名称 | × |
访问器装饰器 | 应用于类的访问器(getter 、setter )上 | 静态方法:类的构造函数 实例方法:类的原型对象 | 属性名称 | 方法描述符对象 |
参数装饰器 | 应用在参数上 | 静态方法:类的构造函数 实例方法:类的原型对象 | 方法名称 | 参数在函数参数列表中的索引 |
装饰器的执行顺序
装饰器工厂
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
| function log(type: string) { return function (target: Function, name: string, descriptor: PropertyDescriptor) {
let value = descriptor.value; descriptor.value = function(x: number, y: number) { let result = value(x, y);
console.log({ type, name, x, y, result });
return result; }
} }
class M { @log('log') static add(x: number, y: number) { return x + y; }
@log('storage') static sub(x: number, y: number) { return x - y; } }
let v1 = M.add(1, 2); console.log(v1); let v2 = M.sub(1, 2); console.log(v2);
export default {}
|
元数据
https://www.npmjs.com/package/reflect-metadata
首先,需要安装 reflect-metadata
1
| npm install reflect-metadata
|
定义元数据
我们可以 类
、方法
等数据定义元数据
- 元数据会被附加到指定的
类
、方法
等数据之上,但是又不会影响 类
、方法
本身的代码
设置
Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey)
metadataKey
:meta 数据的 keymetadataValue
:meta 数据的 值target
:meta 数据附加的目标propertyKey
:对应的 property key
调用方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import "reflect-metadata"
@Reflect.metadata("n", 1) class A { @Reflect.metadata("n", 2) public static method1() { } @Reflect.metadata("n", 4) public method2() { } }
Reflect.defineMetadata('n', 1, A); Reflect.defineMetadata('n', 2, A, 'method1');
let obj = new A(); Reflect.defineMetadata('n', 3, obj); Reflect.defineMetadata('n', 4, obj, 'method2');
console.log(Reflect.getMetadata('n', A)); console.log(Reflect.getMetadata('n', A, ));
|
获取
Reflect.getMetadata(metadataKey, target, propertyKey)
参数的含义与 defineMetadata
对应
使用元数据的 log 装饰器
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
| import "reflect-metadata"
function L(type = 'log') { return function(target: any) { Reflect.defineMetadata("type", type, target); } }
function log(callback: Function) { return function(target: any, name: string, descriptor: PropertyDescriptor) { let value = descriptor.value; let type = Reflect.getMetadata("type", target);
descriptor.value = function(a: number, b: number) { let result = value(a, b); if (type === 'log') { console.log('日志:', { name, a, b, result }) } if (type === 'storage') { localStorage.setItem('storageLog', JSON.stringify({ name, a, b, result })); } return result; } } }
@L('log') class M { @log static add(a: number, b: number) { return a + b; } @log static sub(a: number, b: number) { return a - b; } }
let v1 = M.add(1, 2); console.log(v1); let v2 = M.sub(1, 2); console.log(v2);
|
在 tsconfig.json
中有一个配置 emitDecoratorMetadata
,开启该特性,typescript
会在编译之后自动给 类
、方法
、访问符
、属性
、参数
添加如下几个元数据
- design:type:被装饰目标的类型
- 成员属性:属性的标注类型
- 成员方法:
Function
类型
- design:paramtypes
- 成员方法:方法形参列表的标注类型
- 类:构造函数形参列表的标注类型
- design:returntype
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
| import "reflect-metadata"
function n(target: any) { } function f(name: string) { return function(target: any, propertyKey: string, descriptor: any) { console.log( 'design type', Reflect.getMetadata('design:type', target, propertyKey) ); console.log( 'params type', Reflect.getMetadata('design:paramtypes', target, propertyKey) ); console.log( 'return type', Reflect.getMetadata('design:returntype', target, propertyKey) ); } } function m(target: any, propertyKey: string) {
}
@n class B { @m name: string;
constructor(a: string) {
}
@f('') method1(a: string, b: string) { return 'a' } }
|
编译后
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| __decorate([ m, __metadata("design:type", String) ], B.prototype, "name", void 0); __decorate([ f(''), __metadata("design:type", Function), __metadata("design:paramtypes", [String, String]), __metadata("design:returntype", void 0) ], B.prototype, "method1", null); B = __decorate([ n, __metadata("design:paramtypes", [String]) ], B);
|