TypeScript装饰器学习(二):方法装饰器、属性装饰器

2,727 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

强烈建议先看完:TypeScript装饰器学习(一)

1. 方法装饰器

1. 定义:顾名思义,装饰类方法的装饰器

2. 示例

 // 不带参
 // name buy
 // decr {
 //   value: [Function: buy],
 //   writable: true,
 //   enumerable: false,
 //   configurable: true
 // }
 /**
  * @param  {any} targetClassPrototype - 目标方法所属类的原型对象
 * @param  {string} name - 目标方法的名
 * @param  {PropertyDescriptor} decr - 目标方法的属性描述器
 */
 function methodDecorator(targetClassPrototype: any, name: string, decr: PropertyDescriptor) {
   console.log('targetClassPrototype:', targetClassPrototype);
   console.log('name:', name);
   console.log('decr:', decr);
 }

 // 带参
 // targetClassPrototype {}
 // param-path: /buy
 // name buy
 // decr {
 //   value: [Function: buy],
 //   writable: true,
 //   enumerable: false,
 //   configurable: true
 // }
 function methodParamsDecorator(path: string) {
   console.log('param-path:', path);
   return function (targetClassPrototype: any, name: string, decr: PropertyDescriptor) {
     console.log('targetClassPrototype:', targetClassPrototype);
     console.log('name:', name);
     console.log('decr:', decr);
     decr.value()
   }
 }

 class CustomerServeice {
   name: string = "张三"
   constructor() { }

   @methodParamsDecorator('/buy')
   buy() {
     console.log(`${this.name}购买`);
   }

   pay() {
     console.log(`${this.name}付款`);
   }
 }

3. 方法拦截器

 function MethodInterceptor(params: string) {
   return function (targetClassPrototype: any, name: string, decr: PropertyDescriptor) {
     let temp = decr.value;
     decr.value = function (...args: any) {
       // 前置拦截
       console.log('选择物品');
       temp.call(this, args)
       // 后置拦截
       console.log('吃掉');
     }
   }
 }

 class CustomerServeice {
   name: string
   constructor(name: string) {
     this.name = name;
   }

   @MethodInterceptor('6666')
   buy() {
     console.log(`${this.name}购买`);
   }

   pay() {
     console.log(`${this.name}付款`);
   }
 }

 let c1 = new CustomerServeice('张三')

 // 选择物品
 // 张三购买
 // 吃掉
 c1.buy()

4. 底层实现

 /**
 * @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通过Object.getOwnPropertyDescriptor(target, key)获得该方法的数据属性描述器
   // 参数个数为3,所装饰的是方法参数或属性,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 MethodInterceptor(params) {
   return function (targetClassPrototype, name, decr) {
     var temp = decr.value;
     decr.value = function () {
       var args = [];
       for (var _i = 0; _i < arguments.length; _i++) {
         args[_i] = arguments[_i];
       }
       // 前置拦截
       console.log('选择物品');
       temp.call(this, args);
       // 后置拦截
       console.log('吃掉');
     };
   };
 }
 var CustomerServeice = /** @class */ (function () {
   function CustomerServeice(name) {
     this.name = name;
   }
   CustomerServeice.prototype.buy = function () {
     console.log(this.name + "\u8D2D\u4E70");
   };
   CustomerServeice.prototype.pay = function () {
     console.log(this.name + "\u4ED8\u6B3E");
   };

   // 传入四个参数
   __decorate([
     MethodInterceptor('6666')
   ], CustomerServeice.prototype, "buy", null);
   return CustomerServeice;
 }());
 var c1 = new CustomerServeice('张三');
 // 选择物品
 // 张三购买
 // 吃掉
 c1.buy();

2. 属性装饰器

1. 定义:顾名思义,装饰类属性的装饰器

2. 示例:

 function ProperTYDecorator(param: string) {
   return function (targetClassPrototype: object, name: string | symbol) {
     console.log('targetClassPrototype:', targetClassPrototype);
     console.log("name:", name);
     Object.defineProperty(targetClassPrototype.constructor, "level", {
       configurable: true,
       writable: true,
       enumerable: false,
       value: function () {
         console.log('test');
       }
     })
   }
 }

 class CustomerServeice {
   @ProperTYDecorator('6666')
   name: string
   constructor(name: string) {
     this.name = name;
   }

   buy() {
     console.log(`${this.name}购买`);
   }

   pay() {
     console.log(`${this.name}付款`);
   }
 }

 (CustomerServeice as any).level()