持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情
1. 定义
装饰器就是一个函数,可以注入到类,方法,属性,参数,对象上,对其功能进行拓展。根据装饰器所在位置。可以将装饰器分为:类装饰器,方法装饰器,参数装饰器,元数据装饰器。其他都好理解,但是元数据装饰器又是啥呢?
- 元数据装饰器:元数据是用来描述数据的数据,在定义类、方法时,给他们定义描述信息。
TypeScript
中可以引入reflect-metadata
这个第三方库,调用@Reflect.metadata
来定义元数据。如下:@Reflect.metadata('info', '一些信息')
2. 类装饰器
- 环境搭建
tsconfig.json
中,允许装饰器// 允许普通装饰器 "experimentalDecorators": true, // 允许元数据装饰器 "emitDecoratorMetadata": true,
- 示例
-
不带参数
function CustomerDecorator(targetClass: any) { let customer = new targetClass('张三'); customer.buy() } @CustomerDecorator class CustomerServeice { name: string; constructor(name: string) { this.name = name } buy() { console.log(`${this.name}购买`); } pay() { console.log(`${this.name}付款`); } }
此时控制台输出
张三购买
,此种情况为调用装饰器不带参数,默认传给装饰器方法的是类本身。 -
带参数
function CustomerDecorator(params: string) { return function (targetClass: any) { console.log(params); let customer = new targetClass(); customer.buy() } } @CustomerDecorator('带参类装饰器') class CustomerServeice { name: string = "张三" constructor() { } buy() { console.log(`${this.name}购买`); } pay() { console.log(`${this.name}付款`); } }
此时控制台输出
带参类装饰器 张三购买
那么这里是这么实现的呢? 下面让我们看看通过tsc
生成的JS
源码 -
装饰器
JS
源码/** * @param {Array} decorators - 装饰器数组,可以为一个类或函数添加多个装饰器 * @param {} target - 装饰器所装饰的目标本身 * @param {} key * @param {} desc */ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { // 获取参数个数 var argLength = arguments.length; // targetInfo装饰器最终所装饰的目标本身 // 参数个数为2,所装饰的是类/构造器参数,targetInfo=target(即参数target,类本身) // 参数个数为4,所装饰的是方法【参数desc等于null】,targetInfo为该方法的数据属性 // 参数个数为4,所装饰的是方法参数或属性,targetInfo为undefined var targetInfo = argLength < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc; // decorator用来保存装饰器数组元素 var decorator; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") { // 判断是否为元数据 targetInfo = Reflect.decorate(decorators, target, key, desc); } else { // 遍历装饰目标上的所有装饰器,执行顺序为从下往上 for (var i = decorators.length - 1; i >= 0; i--) { if (decorator = decorators[i]) { // 如果参数个数少于3【decorator为类装饰器或构造器参数装饰器】,执行decorator(targetInfo) // 如果参数个数大于3【decorator为方法装饰器】,执行decorator(target, key, targetInfo) // 如果参数个数等于于3【decorator为方法参数装饰器或属性装饰器】,执行decorator(target, key)) // targetInfo为最终装饰器执行后返回值 targetInfo = (argLength < 3 ? decorator(targetInfo) : argLength > 3 ? decorator(target, key, targetInfo) : decorator(target, key)) || targetInfo }; } } return argLength > 3 && targetInfo && Object.defineProperty(target, key, targetInfo), targetInfo; }; function CustomerDecorator(params) { return function (targetClass) { console.log(params); var customer = new targetClass(); customer.buy(); }; } var CustomerServeice = /** @class */ (function () { function CustomerServeice() { this.name = "张三"; } CustomerServeice.prototype.buy = function () { console.log(this.name + "\u8D2D\u4E70"); }; CustomerServeice.prototype.pay = function () { console.log(this.name + "\u4ED8\u6B3E"); }; // 类装饰器只传入两个参数给__decorate CustomerServeice = __decorate([ // 带参数的先执行函数,获得返回的方法 CustomerDecorator('带参类装饰器') ], CustomerServeice); return CustomerServeice; }());
-
- 应用
/**
* 需求:对已经开发好的项目中的任何一个类创建实例时
* 打印日志信息:输出哪一个类被创建了,并输出传递了哪些参数
*/
function LoggerDecorator<T extends { new(...args: any): any }>(targetClass: T) {
return class extends targetClass {
constructor(...args: any) {
super(...args)
console.log(targetClass.name);
console.log(args);
}
}
}
@LoggerDecorator
class Person {
name: string
constructor(name: string) {
this.name = name
}
}
let p1 = new Person('张三')
此时控制台输出 Person [ '张三' ]
注意:当类装饰器方法为不带参是,类装饰器方法的返回值为void|typeof 类目标
,返回的必须是空或者目标类的子类