TypeScript最佳实践(八)装饰器

258 阅读16分钟

1. 装饰器是什么

  • 它是⼀个表达式
  • 该表达式被执⾏后,返回⼀个函数
  • 函数的⼊参分别为 target、name 和 descriptor
  • 执⾏该函数后,可能返回 descriptor 对象,⽤于配置 target 对象

装饰器就是一个方法或者叫函数,可以注入(写到)到类、方法、属性、参数,对象上,扩展其功能。

课外了解:高阶组件本质上也采用了装饰器的思想

装饰器要解决的问题:

装饰器就是解决在不修改原来类、方法,属性,参数的时候为其添加额外的功能。比如:为整个项目的所有业务类【假如50个类】的所有方法【假如6个方法】都增加日志信息,如果一个一个的增加,那要增加300次日志调用语句,假如日后日志文件格式发生了改变,也还需要修改300次。 如果有了装饰器,只需要修改一次就可以。

有了依赖注入,能大大降低项目的耦合度,大大提升项目的可扩展性。

使用和创建分离是依赖注入的核心思想。

2. 装饰器的分类

  • 类装饰器(Class decorators)
  • 属性装饰器(Property decorators)
  • ⽅法装饰器(Method decorators)
  • 参数装饰器(Parameter decorators)

执行顺序:属性装饰器-->第一个方法参数装饰器-->第一个方法装饰器-->......-->构造器参数装饰器-->类装饰器

3. 类装饰器

接收⼀个参数:

target: TFunction - 被装饰的类

1. 不带参数的类装饰器

function FirstClassDecorator(targetClass: any) {
    Object.keys(targetClass.prototype).forEach((methodname) => {
         console.log("方法", methodname)
         let dataprop = Object.getOwnPropertyDescriptor(targetClass.prototype, methodname)
         console.log("方法数据属性:", dataprop);
    })
}

@FirstClassDecorator
class CustomerService {
    name: string = "下单"
    constructor() {}
    
    buy() {
        console.log(this.name + "购买");
    }
    
    placeOrder() {
        console.log(this.name + "下单购买");
    }
}

2. 带参数类装饰器

function FirstClassDecorator(classinfo: string) {
    return function (targetClass: any) {
        Object.keys(targetClass.prototype).forEach((methodname) => {
            console.log("方法", methodname)
            let dataprop = Object.getOwnPropertyDescriptor(targetClass.prototype, methodname)
            console.log("方法数据属性:", dataprop);
        })
    }
}

3. 底层实现

"use strict";
var __decorate = (this && this.__decorate) ||
  function (decorators, target, key, desc) {
    // argsnum 参数个数
    var argsnum = arguments.length;
    console.log("argsnum:", argsnum)
    // targetinfo 被装饰器修饰的目标【本案例为类】
    // argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
    // argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
    // argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
    var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
    console.log("targetinfo:", targetinfo);
    // decorator保存装饰器数组元素
    var decorator;
    // 元数据信息,支持reflect-metadata元数据
    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)直接执行decorator装饰器,并传递目标targetinfo,这里是类
          // 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo) 
          // 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
          // targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
          console.log("decorator(targetinfo):", decorator(targetinfo))
          targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
            decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
          console.log("targetinforesult:", targetinfo)
        }
      }
    return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
  }
// 底层JS 组合装饰器和目标类 __decorate函数结束
// 不带参数的装饰器
function FirstClassDecorator (targetClass) {
  var targetClassObj = new targetClass();
  targetClassObj.buy();
  console.log("targetClass.name:", targetClass.name);
}
Object.defineProperty(exports, "__esModule", { value: true });
// 带参数的装饰器
function FirstClassDecorator (params) {
  console.log("params:", params);
  return function (targetClass) {
    var targetClassObj = new targetClass();
    targetClassObj.buy();
  };
}
var CustomerService = /** @class */ (function () {
  function CustomerService () {
    this.name = "下单";
  }
  CustomerService.prototype.buy = function () {
    console.log(this.name + "购买");
  };
  CustomerService.prototype.placeOrder = function () {
    console.log(this.name + "下单购买");
  };
  CustomerService = __decorate([
    FirstClassDecorator("我是用来修饰CustomerService类的装饰器参数")
  ], CustomerService);
  return CustomerService;
}());

4. 泛型工厂类继承装饰器

1. demo

function ClassFunctionExtends<T extends { new (...args: any[]): any }> (mytargetClass: T) {
  console.log("mytargetClass", mytargetClass);
  class SonClass extends mytargetClass {
    constructor(...args: any[]) {
      super(args);
      console.log("SonClass执行结束");
    }
    commonMethod() {
      //console.log("this:", this)
      console.log("name:", this.name)
    }
  }
  return SonClass
}

// 2. 目标类
@ClassFunctionExtends
class Test {
  name!: string;
  age!: number
  // 1.先执行原来构造函数
  constructor(name: string) {
    this.name = name;
  }
  eat() {
    console.log(this.name, "吃饭");
  }
}
//let SonClass = ClassFunctionExtends<typeof Test>(Test)
let SonClass = ClassFunctionExtends(Test)
let SonClassObj=new SonClass("wangwu");

2. 实现

// 继承
let __extends = (function (Son, Parent) {

  function getStaticExtendsWithForIn (Son, Parent) {
    for (let key in Parent) {
      if (Object.prototype.hasOwnProperty.call(Parent, key)) {
        Son[key] = Parent[key]
      }
    }
  }

  function getStaticExtendsWithObjectkeys (Son, Parent) {
    Object.keys(Parent).forEach((key) => {
      Son[key] = Parent[key]
    })
  }

  function getStaticExtendsWithProto (Son, Parent) {
    Son.__proto__ = Parent;
  }

  let MyextendStatics = function (Son, Parent) {
    let MyextendStatics = Object.setPrototypeOf || getStaticExtendsWithForIn ||
      getStaticExtendsWithObjectkeys || getStaticExtendsWithProto
    return MyextendStatics(Son, Parent)
  }

  return function (Son, Parent) {
    MyextendStatics(Son, Parent)
    function Middle () {
      this.constructor = Son;
    }
    if (Parent) {//如果不为空 如果父类存在
      Middle.prototype = Parent.prototype;
      Son.prototype = new Middle()
    } else {// 如果父类不存在
      Son.prototype = Object.create(null)
    }
    console.log("Object.create(null):", Object.create(null));
  }
}())

// 2. 底层JS 组合装饰器和目标类 __decorate 函数
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  // argsnum 参数个数
  var argsnum = arguments.length;
  // targetinfo 被装饰器修饰的目标【类或属性或方法或方法参数,本案例为类】
  // argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
  // argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
  // argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
  var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
  // decorator保存装饰器数组元素
  var decorator;
  // 元数据信息,支持reflect-metadata元数据
  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)直接执行decorator装饰器,并传递目标targetinfo,这里是类
        // 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo) 
        // 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
        // targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
        targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
          decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
        console.log("targetinforesult:", targetinfo)
      }
    }
  return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
}

// 2.装饰器类
function ClassFunctionExtends (mytargetClass) {
  console.log("mytargetClass", mytargetClass);
  var SonClass = /** @class */ (function (_super) {
    __extends(SonClass, _super);
    function SonClass () {
      var args = [];
      for (var _i = 0; _i < arguments.length; _i++) {
        args[_i] = arguments[_i];
      }
      var _this = _super.call(this, args) || this;
      console.log("SonClass执行结束");
      return _this;
    }
    SonClass.prototype.commonMethod = function () {
      //console.log("this:", this)
      console.log("name:", this.name);
    };
    return SonClass;
  }(mytargetClass));
  return SonClass;
}

// 目标类
var Test = /** @class */ (function () {
  // 1.先执行原来构造函数
  function Test (name) {
    this.name = name;
    console.log("执行");
    //console.log("beforendame:", this.name)// lisi
  }
  Test.prototype.eat = function () {
    console.log(this.name, "吃饭");
  };
  Test = __decorate([
    ClassFunctionExtends
  ], Test);
  return Test;
}());

// 4.测试
var test = new Test("ok");
// let SonClass = twoDecorator<typeof Test>(Test)
//let SonClass = twoDecorator(Test)
//let SonClassObj=new SonClass("wangwu");

5. 方法装饰器

接收三个参数:

target: Object - 被装饰的类
propertyKey: string | symbol - ⽅法名
descriptor: TypePropertyDescript - 属性描述符

1. demo

// 1 不带参数的方法装饰器
function MyMethodDecorator(targetClassPrototype: any,key: string, methodDecri: PropertyDescriptor) {
  console.log("targetClassPrototype:", targetClassPrototype)//  
  console.log("key:", key);
}

class RoleService {
  public roleName: string = "管理员"
  constructor() {
  }

  @MyMethodDecorator
  DistribRoles() {// 分配角色
    console.log("分配角色.....");
  }
}
export { }

2. 常见场景

在方法装饰器中 拦截目标类的方法, 可以壮大或修改目标类的方法的功能

比如:增加一个日志信息,修改方法参数进行功能扩展处理。

// 增强目标类的方法功能
class StringUtil {//工具类
  public static trimSpace(str: string): string {
    return str.replace(/\s+/g, "")
  }
}

class RoleService {
  public roleName: string = "管理员"
  constructor() {
  }

  @MethodInterceptor("DistribRoles方法")
  DistribRoles(userName: string, isValid: boolean) {// 分配角色
    console.log("分配角色.....");
  }
}

function MethodInterceptor(paramsValue: any) {
  console.log("方法装饰器....");
  return function (targetClassPrototype: any, methodName: any,
    methodDecri: PropertyDescriptor) {

    //targetMethodSave.value 表示原来目标类HttpClient的show()方法
    // 1.1 先保存目标类的方法到targetMethodSave
    console.log("进入方法装饰器:methodDecri:", methodDecri);
    let targetMethodSave = methodDecri.value;
    console.log("targetMethodSave:", targetMethodSave);

    // 1.2.让value函数建立新得函数对象空间 
    //  value建立一个新的函数后,
    // RoleService对象调用DistribRoles;会执行value指向的新函数
    //  并不会执行原来RoleService目标类中DistribRoles方法
    //  这里建立的一个新函数
    methodDecri.value = function (...args: any[]) {
      console.log("this:", this);

      // 迭代所有参数
      args = args.map((arg) => {
        if (typeof arg === "string") {
          return StringUtil.trimSpace(arg);
        }
        return arg;
      })
      console.log(args)

      // 1.4.总结:这是一种典型的用方法装饰器扩大原来方法功能的案例

      // 1.5 但如果增强原来方法功能后,还想继续执行原来RoleService类中DistribRoles方法
      // 使用apply执行targetMethodSave原来函数
      targetMethodSave.apply(this, args)
    }
    //  方法执行之后,继续执行后续代码
    console.log("methodDecri.value:");
  }
}

3. 源码

"use strict";
// 1. 底层JS 组合装饰器和目标类 __decorate函数
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  // argsnum 参数个数
  var argsnum = arguments.length;
  // targetinfo 被装饰器修饰的目标【类或属性或方法或方法参数,本案例为类】
  // argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
  // argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
  // argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
  var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
  // decorator保存装饰器数组元素
  var decorator;
  // 元数据信息,支持reflect-metadata元数据
  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)直接执行decorator装饰器,并传递目标targetinfo,这里是类
        // 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo) 
        // 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
        // targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
        targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
          decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
        console.log("targetinforesult:", targetinfo)
      }
    }
  return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
}
Object.defineProperty(exports, "__esModule", { value: true });
// 底层JS 组合装饰器和目标类 __decorate函数结束

// 2. 工具类
var StringUtil = /** @class */ (function () {
  function StringUtil () {
  }
  StringUtil.trimSpace = function (str) {
    return str.replace(/\s+/g, "");
  };
  return StringUtil;
}());

// 目标类
var RoleService = /** @class */ (function () {
  function RoleService () {
    this.roleName = "管理员";
  }
  RoleService.prototype.DistribRoles = function (userName, isValid) {
    console.log("分配角色.....");
  };
  __decorate([
    MethodInterceptor("DistribRoles方法")
  ], RoleService.prototype, "DistribRoles", null);
  return RoleService;
}());

// 3. 装饰器方法
function MethodInterceptor (paramsValue) {
  console.log("方法装饰器....");
  return function (targetClassPrototype, methodName, methodDecri) {
    //targetMethodSave.value 表示原来目标类HttpClient的show()方法
    // 1.1 先保存目标类的方法到targetMethodSave
    console.log("进入方法装饰器:methodDecri:", methodDecri);
    var targetMethodSave = methodDecri.value;
    console.log("targetMethodSave:", targetMethodSave);
    // 1.2.让value函数建立新得函数对象空间 
    //  value建立一个新的函数后,
    // RoleService对象调用DistribRoles;会执行value指向的新函数
    //  并不会执行原来RoleService目标类中DistribRoles方法
    //  这里建立的一个新函数就和后端 Java的spring AOP中的方法拦截器思想就完全一样
    methodDecri.value = function () {
      var args = [];
      for (var _i = 0; _i < arguments.length; _i++) {
        args[_i] = arguments[_i];
      }
      console.log("this:", this);
      // 迭代所有参数
      args = args.map(function (arg) {
        if (typeof arg === "string") {
          return StringUtil.trimSpace(arg);
        }
        return arg;
      });
      console.log(args);
      // 1.4.总结:这是一种典型的用方法装饰器扩大原来方法功能的案例
      // 1.5 但如果增强原来方法功能后,还想继续执行原来RoleService类中DistribRoles方法
      // 使用apply执行targetMethodSave原来函数
      targetMethodSave.apply(this, args);
    };
    //  方法执行之后,继续执行后续代码
    console.log("methodDecri.value:");
  };
}

6. 属性装饰器

接收两个参数:

target: Object - 被装饰的类
propertyKey: string | symbol - 被装饰类的属性名

1. demo

// 属性装饰器【这里先学会使用,后面实战再深入】
function loginProperty(attrValue: any) {
  return function (targetclassPrototype: object, attrname: string | symbol) {
    console.log("targetclassPrototype:", targetclassPrototype);
    console.log("attrname:", attrname);
    (targetclassPrototype.constructor as any).custLevelDescri = function () {
      console.log("消费5000元升级为贵宾");
      console.log("消费10000元升级为贵宾,赠送微波炉一个");
    }
  }
}

// 顾客目标类
class CustomerService {
  public custname: string = "王五"
  @loginProperty("顾客登记")
  public degree!: string
  constructor() {
  }

  show() {
    console.log("顾客名:", this.custname)
  }
}

(CustomerService as any).custLevelDescri()

2. 源码

// 1. 底层JS 组合装饰器和目标类 __decorate 函数
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  // argsnum 参数个数
  var argsnum = arguments.length;
  // targetinfo 被装饰器修饰的目标【类或属性或方法或方法参数,本案例为类】
  // argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
  // argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
  // argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
  var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
  // decorator保存装饰器数组元素
  var decorator;
  // 元数据信息,支持reflect-metadata元数据
  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)直接执行decorator装饰器,并传递目标targetinfo,这里是类
        // 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo) 
        // 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
        // targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
        targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
          decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
        console.log("targetinforesult:", targetinfo)
      }
    }
  return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
}
// 底层 JS 组合装饰器和目标类 __decorate 函数结束

// 2. 属性装饰器
function loginProperty(attrValue) {
    return function (targetclassPrototype, attrname) {
        console.log("targetclassPrototype:", targetclassPrototype);
        console.log("attrname:", attrname);
        targetclassPrototype.constructor.custLevelDescri = function () {
            console.log("消费5000元升级为贵宾");
            console.log("消费10000元升级为贵宾,赠送微波炉一个");
        };
    };
}

// 3.目标类
var CustomerService = /** @class */ (function () {
    function CustomerService() {
        this.custname = "王五";
    }
    CustomerService.prototype.show = function () {
        console.log("顾客名:", this.custname);
    };
    __decorate([
        loginProperty("顾客登记")
    ], CustomerService.prototype, "degree");
    return CustomerService;
}());
CustomerService.custLevelDescri();

7. 参数装饰器

接收三个参数:

target: Object - 被装饰的类
propertyKey: string | symbol - ⽅法名
parameterIndex: number - ⽅法中参数的索引值

1. demo

function UrlParam(params: any) {
  return function paramDecorator(targetClassPrototype: any,
    methodname: string, paramindex: number) {
    console.log("targetClassPrototype:", targetClassPrototype)
    console.log("methodname:", methodname);
    console.log("paramindex:", paramindex);
    targetClassPrototype.info = params
  }
}
class People {
  eat(@UrlParam("地址信息") address: string, who: string) {
    console.log("address:", address);
  }
}

2. 源码

"use strict";
// 1. 底层JS 组合装饰器和目标类 __decorate函数
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  // argsnum 参数个数
  var argsnum = arguments.length;
  // targetinfo 被装饰器修饰的目标【类或属性或方法或方法参数,本案例为类】
  // argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
  // argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
  // argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
  var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
  // decorator保存装饰器数组元素
  var decorator;
  // 元数据信息,支持reflect-metadata元数据
  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)直接执行decorator装饰器,并传递目标targetinfo,这里是类
        // 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo) 
        // 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
        // targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
        targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
          decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
        console.log("targetinforesult:", targetinfo)
      }
    }
  return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
}
// 底层JS 组合装饰器和目标类 __decorate函数结束


// Object.defineProperty(exports, "__esModule", { value: true });
// 2 带参数的装饰器
function UrlParam(params) {
    return function paramDecorator(targetClassPrototype, methodname, paramindex) {
        console.log("targetClassPrototype:", targetClassPrototype);
        console.log("methodname:", methodname);
        console.log("paramindex:", paramindex);
        targetClassPrototype.info = params;
    };
}

// 3 封装带参数装饰器
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var People = /** @class */ (function () {
    function People() {
    }
    People.prototype.eat = function (address, who) {
        console.log("address:", address);
    };
    __decorate([
        __param(0, UrlParam("地址信息"))
    ], People.prototype, "eat", null);
    return People;
}());

8. 装饰器执行顺序

属性装饰器-->第一个方法参数装饰器-->第一个方法装饰器-->......-->构造器参数装饰器-->类装饰器

1. demo

// 1.  类丶属性丶方法 丶参数装饰器组合案例实现   2.  执行顺序 
function firstMethodDecorator(targetClassPrototype: any,
  methodname: string) {
  console.log("=============执行第一个方法装饰器==============")
  console.log("类名:", targetClassPrototype)//  类原型对象变量   URLInfo { show: [Function] }
  console.log("方法名:", methodname);//key
}


function secondMethodDecorator(params: string) {
  return function (targetClassPrototype: any, methodname: string) {
    console.log("=============执行第二个方法装饰器==============")
    console.log("类名:", targetClassPrototype)//  类原型对象变量   URLInfo { show: [Function] }
    console.log("方法名:", methodname);//key
  }
}

function paramDecorator(targetClassPrototype: any, paramname: string, paramindex: number) {
  console.log("=============执行参数装饰器==============")
  console.log("targetClassPrototype:", targetClassPrototype);
  console.log("参数名:", paramname);
  console.log("参数索引:", paramindex);
}


function UrlPropDecorator(targetClassPrototype: any, attrname: any) {

  console.log("=============执行属性装饰器==============")
  console.log("targetClassPrototype:", targetClassPrototype);
  console.log("属性名:", attrname);
}


function URLInfoDecorator(targetClassPrototype: any) {
  console.log("==========类装饰器============")
  console.log("targetClassPrototype:", targetClassPrototype);
}

function constructorDecorator(params: any) {
  return function (targetClassPrototype: any, paramname: string, paramindex: number) {
    console.log("==========构造器参数装饰器============")
    console.log("构造器参数装饰器", targetClassPrototype);
    console.log("构造器参数名为:", paramname);
    console.log("构造器参数索引位置:", paramindex);
  }
}

@URLInfoDecorator
class URLInfo {
  constructor(@constructorDecorator("url") public uri: string) {

  }

  @UrlPropDecorator
  public url: string = "https://www.imooc.com"


  @firstMethodDecorator
  methodOne(@paramDecorator data: string) {
    console.log("this:", this);
    console.log("目标类:", this.uri)
  }

  @secondMethodDecorator("yes")
  methodTwo(@paramDecorator address: string) {
    console.log(address)
  }
}
export { }

2. 源码

// 1. 底层JS 组合装饰器和目标类 __decorate函数
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  // argsnum 参数个数
  var argsnum = arguments.length;
  // targetinfo 被装饰器修饰的目标【类或属性或方法或方法参数,本案例为类】
  // argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
  // argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
  // argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
  var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
  // decorator保存装饰器数组元素
  var decorator;
  // 元数据信息,支持reflect-metadata元数据
  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)直接执行decorator装饰器,并传递目标targetinfo,这里是类
        // 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo) 
        // 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
        // targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
        targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
          decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
        console.log("targetinforesult:", targetinfo)
      }
    }
  return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
}
// 底层JS 组合装饰器和目标类 __decorate函数结束

function firstMethodDecorator(targetClassPrototype, methodname) {
    console.log("=============执行第一个方法装饰器==============");
    console.log("类名:", targetClassPrototype); //  类原型对象变量   URLInfo { show: [Function] }
    console.log("方法名:", methodname); //key
}
function secondMethodDecorator(params) {
    return function (targetClassPrototype, methodname) {
        console.log("=============执行第二个方法装饰器==============");
        console.log("类名:", targetClassPrototype); //  类原型对象变量   URLInfo { show: [Function] }
        console.log("方法名:", methodname); //key
    };
}
function paramDecorator(targetClassPrototype, paramname, paramindex) {
    console.log("=============执行参数装饰器==============");
    console.log("targetClassPrototype:", targetClassPrototype);
    console.log("参数名:", paramname);
    console.log("参数索引:", paramindex);
}
function UrlPropDecorator(targetClassPrototype, attrname) {
    console.log("=============执行属性装饰器==============");
    console.log("targetClassPrototype:", targetClassPrototype);
    console.log("属性名:", attrname);
}
function URLInfoDecorator(targetClassPrototype) {
    console.log("==========类装饰器============");
    console.log("targetClassPrototype:", targetClassPrototype);
}
function constructorDecorator(params) {
    return function (targetClassPrototype, paramname, paramindex) {
        console.log("==========构造器参数装饰器============");
        console.log("构造器参数装饰器", targetClassPrototype);
        console.log("构造器参数名为:", paramname);
        console.log("构造器参数索引位置:", paramindex);
    };
}
var URLInfo = /** @class */ (function () {
    function URLInfo(uri) {
        this.uri = uri;
        this.url = "https://www.imooc.com";
    }
    URLInfo.prototype.methodOne = function (data) {
        console.log("this:", this);
        console.log("目标类:", this.uri);
    };
    URLInfo.prototype.methodTwo = function (address) {
        console.log(address);
    };
    __decorate([
        UrlPropDecorator
    ], URLInfo.prototype, "url", void 0);
    __decorate([
        firstMethodDecorator,
        __param(0, paramDecorator)
    ], URLInfo.prototype, "methodOne", null);
    __decorate([
        secondMethodDecorator("yes"),
        __param(0, paramDecorator)
    ], URLInfo.prototype, "methodTwo", null);
    URLInfo = __decorate([
        URLInfoDecorator,
        __param(0, constructorDecorator("url"))
    ], URLInfo);
    return URLInfo;
}());

9. reflect-metadata

1. 什么是元数据?

元数据指附加在对象、类、方法、属性丶参数上的数据。

2. 元数据作用

元数据用来帮助提供实现某种业务功能需要用到的数据。

Reflect.defineMetadata

Reflect.defineMetadata 是一个重载的方法

// 1.1 为类或者对象上定义元数据
 Reflect.defineMetadata(metakey,metavalue,targetClassOrObject)
//  1.2 为方法定义元数据
 Reflect.defineMetadata(metakey,metavalue,targetprototype,methodname)
//  1.3 为属性定义元数据
 Reflect.defineMetadata(metakey,metavalue,targetprototype,propkey)

// 说明:打开 d.ts 定义描述文件说明:Reflect 是命名空间,defineMetadata 是命名空间中的一个方法。

// 1.4 在对象上定义元数据
Reflect.defineMetadata('firstdescribe', '对象属性全部符合要求', obj);
Reflect.defineMetadata('seconddescribe', '对象不可删除', obj);

//   获取obj上metakey为 firstdescribe 的值
console.log(Reflect.getMetadata('firstdescribe', obj))// 输出对象属性全部符合要求

//  获取obj上metakey不存在的值
console.log(Reflect.getMetadata('threedescribe', obj))// 输出undefined

//  1.5  使用 Reflect.defineMetadata 在对象属性上定义元数据。
//  在对象属性上定义和获取元数据
Reflect.defineMetadata('usernamemetakey', '用户名合法', obj,"username");
Reflect.getMetadata('usernamemetakey', obj, "username"));// 输出用户名合法

// 使用 Reflect.hasMetadata 查看对象或对象属性上是否存在某个元数据
if (Reflect.hasMetadata('describe', obj)) {
  console.log("obj存在describe元数据");
}

在类,方法上定义元数据

// 1. 在类上定义元数据
@Reflect.metadata('decribe', '都是地球人')
class People {
  @Reflect.metadata("descible", "姓名不能包含非法汉字")
  username = "wangwu"
  @Reflect.metadata("importinfo", "去吃陶然居好吗")
  eat() {

  }
}
// 2
// 2.1 获取类上的元数据
console.log(Reflect.getMetadata('decribe', People));// 都是地球人

// 2.2 获取方法上的元数据 第二个参数是原型
console.log(Reflect.getMetadata('importinfo', People.prototype, 'eat'));//去吃陶然居好吗

// 2.3 判断People.prototype 原型上 eat 方法上是否存在importinfo元数据
if (Reflect.hasMetadata('importinfo', People.prototype, 'eat')) {
  console.log("hasMetadata=>People原型上存在eat方法的importinfo元数据");
}

// 3 定义子类
class ChinesePeople extends People {
  guoYear() {

  }
}

//  4 子类获取父类原型上的方法 ———— hasMetadata
if (Reflect.hasMetadata('importinfo', ChinesePeople.prototype, 'eat')) {
  console.log("hasMetadata=>ChinesePeople原型上通过继承也获取到了eat方法和eat方法的importinfo元数据");
}

//  5  获取自有元数据,但不能获取原型链上父类的元数据 ———— hasOwnMetadata
if (Reflect.hasOwnMetadata('importinfo', ChinesePeople.prototype, 'eat')) {
  console.log("hasOwnMetadata=>ChinesePeople原型上存在eat方法的importinfo元数据");
} else {
  console.log("hasOwnMetadata=>ChinesePeople原型上不存在eat方法的importinfo元数据");
}

直接在类属性上定义元数据

// 为类定义元数据
@Reflect.metadata("info", "地球人")
class People {
  @Reflect.metadata('descible1', '居住地为主要城市')
  @Reflect.metadata('descible2', '上海')
  place: Array<string> = ["中国", "北京"]

  @Reflect.metadata('firstname', '第一个名字')
  @Reflect.metadata('lastname', '最后一个名字')
  getFullName(name: string, age: string): number {
    return 100
  }
}

// 获取元数据
console.log(Reflect.getMetadata('info', People));//地球人
console.log(Reflect.getMetadata("descible", People.prototype, 'place'));//rose
console.log(Reflect.getMetadata('firstname', People.prototype, 'getFullName'))//Jim
console.log(Reflect.getMetadata('lastname', People.prototype, 'getFullName'))//Jim
// [
//   'design:returntype',
//   'design:paramtypes',
//   'design:type',
//   'lastname',
//   'firstname'
// ]
// // 获取People.prototype 上getFullName方法的全部元数据Key组成的数组
//console.log(Reflect.getMetadataKeys(People.prototype, "getFullName"));
// Reflect.getMetadataKeys(People.prototype).forEach((item) => {
//   console.log("metadatakey:", item);
// })
// Reflect.getMetadataKeys(People.prototype, 'getFullName').forEach((metakey) => {
//   console.log("11metadatakey:", metakey);
//   console.log(Reflect.getMetadata(metakey, People.prototype, 'getFullName'));
// })

// 获取People类上place方法的全部元数据Key组成的数组
// 输出
// [
//   'design:type',
//   'descible1',
//   'descible2'
// ]
console.log(Reflect.getMetadataKeys(People.prototype, "place"));

Reflect.getMetadataKeys(People.prototype, 'place').forEach((metakey) => {
  console.log("属性metadatakey:", metakey);
  console.log(Reflect.getMetadata(metakey, People.prototype, 'place'));
})


class ChinesePeople extends People {
  @Reflect.metadata("descible", "姓名不能包含非法汉字")
  guoYear(args: string) {

  }
}
console.log("getMetadataKeys==>查看父类上的方法...");
console.log(Reflect.getMetadataKeys(ChinesePeople.prototype, 'getFullName'))//Jim
console.log("getOwnMetadataKeys不能查看父类上的方法...");
console.log(Reflect.getOwnMetadataKeys(ChinesePeople.prototype, 'getFullName'))//Jim

在装饰器内部为类,方法,属性定义或者获取元数据

// 直接为类或属性或方法定义元数据
// 为类定义元数据
@Reflect.metadata('decribe', '都是地球人')
class People {
  username = "wangwu"

  eat() {

  }
}

@Reflect.metadata('decribe', '木星和太阳')
class Customer {
  username = "wangwu"
  eat() {

  }
}


// 获取元数据
console.log(Reflect.getMetadata('decribe', People));// 都是地球人

3 个重要且特殊的内置元数据

  1. design:paramtypes
    a. 构造器所有参数数据类型组成的数组
    b. 类中方法全部参数的数据类型组成的数组
  2. design:type
    a. 获取类属性的数据类型
    b. 获取类方法参数的数据类型
  3. design:returntype
    a. 获取类方法返回值的数据类型