定义
- 装饰器本质是一个函数
- 通过 @ 语法将函数应用到类、类的属性、方法、访问器或参数上
- 在代码文件运行时被调用,对被装饰的目标进行扩展、修改或注入额外逻辑
装饰器分类
- 类装饰器(Class Decorator)
- 属性装饰器(Property Decorator)
- 方法装饰器(Method Decorator)
- 参数装饰器(Parameter Decorator)
- 访问器装饰器(Accessor Decorator)
// 类装饰器
function classDecorator1(cls) {}
function classDecorator2(cls) {}
// 属性装饰器
function propertyDecorator1(clsPrototype, attrName) {}
function propertyDecorator2(clsPrototype, attrName) {}
// 访问器装饰器
function accessorDecorator1(clsPrototype, accessorName, descriptor) {}
function accessorDecorator2(clsPrototype, accessorName, descriptor) {}
// 方法参数装饰器
function paramDecorator1(clsPrototype, methodName, index) {}
function paramDecorator2(clsPrototype, methodName, index) {}
// 方法装饰器
function methodDecorator1(clsPrototype, methodName, descriptor) {}
function methodDecorator2(clsPrototype, methodName, descriptor) {}
// 装饰器的应用
@classDecorator1
@classDecorator2
class Example {
@propertyDecorator1
@propertyDecorator2
property: string = '';
@accessorDecorator1
@accessorDecorator2
get value() { return this.property; }
@methodDecorator1
@methodDecorator2
method(
@paramDecorator1 param1: string,
@paramDecorator2 param2: number
) {}
}
装饰器工厂
用于生产装饰器函数的工厂,通过闭包的方式返回装饰器函数,使装饰器函数运行时可以拿到外部传入的数据,以下以类装饰工厂举例,其余装饰器函数工厂也是同理
function classDecoratorFactory(params) {
return function (cls) {}
}
@classDecoratorFactory('age')
class Example {}
装饰器原理
思路
ts 是 js 的超集,本质上还是通过 js 实现的,装饰器是 ts 独有的语法,可通过将 ts 编译为 js,查看装饰器的实现原理
实现
- 修改 ts 配置文件,使编译结果符合预期
// tsconfig.json
{
"compilerOptions": {
"target": "ES5", // 编译目标为 es5
"experimentalDecorators": true // 告诉编译器使用了实验性的装饰器特性,需对装饰器特性进行编译
}
}
- 命令行中执行编译命令
tsc
- 得到编译后的 js 代码
"use strict";
/**
* 装饰逻辑
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
// 参数个数
// c < 3 ? 类装饰器: (desc === null ? 其他装饰器 : 属性装饰器)
var c = arguments.length;
// 装饰器间传递的对象
var r = c < 3 ?
target : // 类构造函数
desc === null ?
desc = Object.getOwnPropertyDescriptor(target, key) : // 属性描述符对象
desc; // void 0
// 当前执行的装饰器函数
var d;
// 核心逻辑
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
r = Reflect.decorate(decorators, target, key, desc); // 引入 reflect-metadata 包后才有该方法
} else { // 回退逻辑
for (var i = decorators.length - 1; i >= 0; i--) {
if (d = decorators[i]) {
// 装饰器函数要将 r 返回,供给下一个装饰器使用
// 否则后面的装饰器还是基于原先的 r 进行处理
r = (c < 3
? d(r)
: c > 3
? d(target, key, r)
: d(target, key))
|| r;
}
}
}
// 若为函数/函数参数/访问器装饰器,需更新属性描述符对象
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
/**
* 参数装饰器的包装函数:通过闭包的形式传递参数索引
*/
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) {
decorator(target, key, paramIndex);
}
};
function classDecorator1(cls) { }
function classDecorator2(cls) { }
function propertyDecorator1(clsPrototype, attrName) { }
function propertyDecorator2(clsPrototype, attrName) { }
function accessorDecorator1(clsPrototype, accessorName, descriptor) { }
function accessorDecorator2(clsPrototype, accessorName, descriptor) { }
function paramDecorator1(clsPrototype, methodName, index) { }
function paramDecorator2(clsPrototype, methodName, index) { }
function methodDecorator1(clsPrototype, methodName, descriptor) { }
function methodDecorator2(clsPrototype, methodName, descriptor) { }
var Example = /** @class */ (function () {
function Example() {
this.property = '';
}
Object.defineProperty(Example.prototype, "value", {
get: function () { return this.property; },
enumerable: false,
configurable: true
});
Example.prototype.method = function (param1, param2) { };
__decorate([
propertyDecorator1,
propertyDecorator2
], Example.prototype, "property", void 0);
__decorate([
accessorDecorator1,
accessorDecorator2
], Example.prototype, "value", null);
__decorate([
methodDecorator1,
methodDecorator2,
__param(0, paramDecorator1),
__param(1, paramDecorator2)
], Example.prototype, "method", null);
Example = __decorate([
classDecorator1,
classDecorator2
], Example);
return Example;
}());
装饰器执行顺序
- 成员装饰器优先于类装饰器,成员装饰器根据声明位置执行
- 相同装饰目标的多个装饰器:从下到上执行(最靠近目标的先执行)
- 同一函数多个参数的装饰器:按声明顺序从右到左执行
// property 的装饰器(从下到上执行)
@propertyDecorator2 // 第1个执行
@propertyDecorator1 // 第2个执行
// method() 的参数装饰器
@paramDecorator2 // 第3个执行
@paramDecorator1 // 第4个执行
// method() 的装饰器(从下到上执行)
@methodDecorator2 // 第5个执行
@methodDecorator1 // 第6个执行
// method2() 的参数装饰器
@paramDecorator4 // 第7个执行
@paramDecorator3 // 第8个执行
// method2() 的装饰器(从下到上执行)
@methodDecorator4 // 第9个执行
@methodDecorator3 // 第10个执行
// get value() 的装饰器(从下到上执行)
@accessorDecorator2 // 第11个执行
@accessorDecorator1 // 第12个执行
// 类装饰器(从下到上执行)
@classDecorator2 // 第13个执行
@classDecorator1 // 第14个执行
元数据
元数据(Metadata)是指描述数据的数据
元数据 API
ts 本身不直接提供元数据 API,需要依赖 reflect-metadata 库
pnpm add reflect-metadata
import 'reflect-metadata';
// 给数据定义元数据
Reflect.defineMetadata(metadataKey, metadataValue, target)
Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey)
// 获取数据的元数据
let result = Reflect.getMetadata(metadataKey, target)
let result = Reflect.getMetadata(metadataKey, target, propertyKey)
// 数据是否存在指定的元数据
let result = Reflect.hasMetadata(metadataKey, target)
let result = Reflect.hasMetadata(metadataKey, target, propertyKey)
// 获取数据上所有元数据的建
let result = Reflect.getMetadataKeys(target)
let result = Reflect.getMetadataKeys(target, propertyKey)
// 删除数据上的元数据
let result = Reflect.deleteMetadata(metadataKey, target)
let result = Reflect.deleteMetadata(metadataKey, target, propertyKey)
装饰器工厂:返回定义元数据的装饰器函数
Reflect.metadata(metadataKey, metadataValue)
// => 简化实现
function metadata(metadataKey: any, metadataValue: any) {
function decorator(target: Function | Object, propertyKey?: string | symbol) {
if (typeof propertyKey !== "undefined") {
// 应用于属性或方法
Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey);
} else {
// 应用于类
Reflect.defineMetadata(metadataKey, metadataValue, target);
}
}
return decorator;
}
// 挂载到 Reflect 上
Reflect.metadata = metadata;
内置元数据
在 tsconfig.json 里配置 emitDecoratorMetadata 选项,会给所有装饰器修饰的内容自动添加内置元数据
- design:type
- design:paramtypes
- design:returntype
上述元数据在不同装饰器中有不同的含义
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
__decorate([
propertyDecorator1,
propertyDecorator2,
__metadata("design:type", String) // 属性类型
], Example.prototype, "property", void 0);
__decorate([
accessorDecorator1,
accessorDecorator2,
__metadata("design:type", Object), // 编译器在编译时无法完全确定返回类型,所以默认使用 Object 作为类型
__metadata("design:paramtypes", []) // 因为 getter 访问器不接受任何参数,所以参数类型数组为空
], Example.prototype, "value", null);
__decorate([
methodDecorator1,
methodDecorator2,
__param(0, paramDecorator1),
__param(1, paramDecorator2),
__metadata("design:type", Function), // 固定为函数类型
__metadata("design:paramtypes", [String, Number]), // 参数类型
__metadata("design:returntype", void 0) // 返回类型
], Example.prototype, "method", null);
Example = __decorate([
classDecorator1,
classDecorator2
], Example);