第五章:从装饰器应用到底层JS深入+装饰器实战

291 阅读18分钟

慕课网 TS 高级课程

第五章:从装饰器应用到底层JS深入+装饰器实战

技能大纲

10-1 本章概述,熟练透彻掌握装饰器对职业发展有何意义?
10-2 【装饰器概述】装饰器定义,分类,要解决的问题
10-3 【类装饰器】类装饰器的两种实现
10-4 【类装饰器底层源码】逐行深剖底层 JS 源码
10-5 【泛型工厂类继承装饰器】 泛型工厂类继承装饰器实现和意义
10-6 【泛型工厂类继承装饰器底层源码】逐行深剖+优化底层 JS 源码

10-7 【泛型工厂类匿名类+继承装饰器】匿名类在装饰器中的实现

10-8 【方法装饰器】 拦截器意义,如何实现前置丶后置拦截器功能?【真实应用场景】
10-9 【方法装饰器底层源码】逐行深剖底层 JS 源码

10-10 【方法装饰器底层源码 】方法拦截功能功能为什么会失效?

10-11 【属性装饰器】属性装饰器应用——顾客等级说明实现 1

10-11 【属性装饰器底层源码】 逐行解析底层JS源码 2

10-12 【属性丶类丶方法装饰器综合应用】依赖注入+请求方法,控制器初步实现

10-13 【原型方法参数装饰器】参数装饰器实现
10-14 【参数装饰器底层源码】 逐行解析底层JS源码

10-15 【构造器参数装饰器】构造器参数装饰器注入实现

10-16 【多个装饰器组合执行】 类丶属性丶方法 丶参数装饰器组合+执行顺序

10-17 【元数据】 理解 reflect-metadata 和 常见元数据操作方法

10-18 [元数据] 3个重要且特殊的内置元数据key

10-19 【仿 Nestjs 装饰器实战准备】依赖注入和它的好处

10-20 【仿 Nestjs 装饰器实战准备】 项目分层

10-21 【仿 Nestjs 装饰器实战】 单件设计模式构建 Collection

10-22 【仿 Nestjs装饰器实战】控制器实现

10-23 【仿 Nestjs 装饰器实战】 业务逻辑层装饰器构建

10-24 【仿 Nestjs 装饰器实战】 依赖注入和自动装配装饰器构建

10-25 【仿 Nestjs 装饰器实战】 升级——函数重载+业务逻辑层装饰器组合实战

10-26 【仿 Nestjs 装饰器实战】优化: 元数据如何替换容器保存功能

10-27 【仿 Nestjs 装饰器实战】控制器装饰器+路由器功能实现

10-28 【仿 Nestjs装饰器实战】多种请求方法装饰器实现
10-29 【仿 Nestjs装饰器实战】中间件装饰器实现
10-30 【仿 Nestjs装饰器实战】多个方法多个中间件装饰器如何执行?
10-31 【仿 Nestjs装饰器实战】业务逻辑层装饰器+控制器装饰器 构建大中项目分层架构

10-32 【仿 Nestjs装饰器实战+深度思考题+作业】 数据模型层实现丶优化+优化依赖注入对象调用

慕课网 TS 高级课程

10-1 本章概述,掌握装饰器对前端有什么好处?

装饰器是前端了不起的技术革命,弥补了只有后端语言才有 AOP【类似装饰器】的短板,学习装饰器好处有:

1 较大提升前端架构思维和前端架构能力,装饰器底层蕴含的拦截器思想在 Java Spring, Nestjs框架,python 各种后端语言中都有广泛的应用,而拦截器展示的就是一种架构思维,通过学习装饰器能扩大技术视野,是作为一名前端架构师以及更高职位必会技能。

2 Nestjs 等相对新型的 Nodejs 框架大量运用了 TS 装饰器, 例如: @Controller @Service @Get @Post

3 在面试中,如果告诉面试官,你精通装饰器,这也能成为你的大加分项,因为公司更需架构思维能力强的前端工程师,因为具有架构思维的前端开发人员在大中项目中一定能写出扩展性更好的代码。

纵观本章,囊括了装饰器应用,装饰器底层JS源码,装饰器实战。

慕课网 TS 高级课程

10-2 【装饰器概述】装饰器定义,分类,要解决的问题
1 装饰器定义:

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

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

2 装饰器要解决的问题:

装饰器就是解决在不修改原来类、方法,属性,参数的时候为其添加额外的功能。比如:为整个项目的所有业务类【假如50个类】的所有方法【假如6个方法】都增加日志信息,如果一个一个的增加,那要增加300次日志调用语句,假如日后日志文件格式发生了改变,也还需要修改300次。 如果有了装饰器,只需要修改一次就可以。这个属于项目中的通用功能,大家了解下即可,后面我们仿 Nestjs 实战 时对装饰器的这个特性会有很深的体会。

在 Nestjs 中 装饰器可以解决依赖注入的问题,而依赖注入是 Java等后端语言拥有的非常优秀的编程思想,

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

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

3. 装饰器分类:

常见的装饰器:类装饰器、属性装饰器、方法装饰器、参数装饰器,元数据装饰器。

4. 元数据装饰器初步理解:

元数据装饰器:在定义类或者类方法或者对象的时候,可以设置一些元数据,我们可以获取到在类与类方法上添加的元数据,需要引入 reflect-metadata 第三方库 采用 @Reflect.metadata来实现。元数据指的是描述东西时用的数据,例如:Reflect.metadata("importinfo", "疫情期间用公筷,戴口罩")。

5. 装饰器两种写法:

**写法1:**让调用时(使用时) 不传递参数的装饰器

写法2:装饰器工厂【让调用时 可以传递参数的装饰器】。

10-3 【类装饰器】类装饰器实现

课程安排: 1. 环境搭建 2. 类装饰器两种实现

1. 环境搭建

  1. 安装 concurrently 支持合并执行,同时运行多个 script 命令:

    cnpm  i  concurrently -S 或 yarn add  concurrently -S
    cnpm  i  nodemon -S 或 yarn add  nodemon -S
    
  2. tsconfig.json 文件修改如下:

     --编译输入输出目录	
         "outDir":"./dist",                             
         "rootDir":"./src", 
     -- 消除装饰器警告
     "experimentalDecorators": true,          
     "emitDecoratorMetadata": true,  
    
  3. 配置 package.json 文件脚本信息

    "scripts": {
    
     "dev:build": "tsc -w", 
         
      --监控 dist/teaching 目录中的 js 文件,变化时执行 node 命令     
     "dev:start":"nodemon --watch dist/teaching js --exec  node ./dist/teaching/1ClassDecorator.js",
         
      --合并启动     
     "start":  "concurrently npm:dev:*"
     
      --命令解决 typescript 编译装饰器类时出现的 bug 
      "tsc":"tsc src/teaching/1ClassDecorator.ts  --target ES5 -w --experimentalDecorators"
        
      --本章后面章节会用到,先配置上
     "ctrl":"ts-node src/controller/HomeController.ts",
    "beginapp": "nodemon --watch src/ -e ts --exec ts-node ./src/expressapp.ts",
     }
    
  4. 类装饰器两种实现 [带参数和不带参数]

// 不带参数的类装饰器案例:
function FirstClassDecorator(targetClass: any) {
    console.log(targetClass.prototype.constructor.name + "信息");
    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 + "下单购买");
  }
}

// 带参数类装饰器案例
function FirstClassDecorator(classinfo: string) {
  return function (targetClass: any) {
    console.log(targetClass.prototype.constructor.name + "信息");
    Object.keys(targetClass.prototype).forEach((methodname) => {
      console.log("方法", methodname)
      let dataprop = Object.getOwnPropertyDescriptor(targetClass.prototype, methodname)
      console.log("方法数据属性:", dataprop);
    })
  }
}
10-4 【类装饰器底层源码】逐行深剖底层 JS 源码

课程安排:逐行深剖底层 JS 源码

"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函数结束

// 2. 传递参数的类装饰器
function FirstClassDecorator (classinfo) {
  return function (targetClass) {
    console.log(targetClass.prototype.constructor.name + "信息");
    Object.keys(targetClass.prototype).forEach(function (methodname) {
      console.log("方法", methodname);
      var dataprop = Object.getOwnPropertyDescriptor(targetClass.prototype, methodname);
      console.log("方法数据属性:", dataprop);
    });
    //return 3
  };
}

// 3.目标类
var Test = /** @class */ (function () {
  function Test () {
    this.name = "下单";
  }
  Test.prototype.buy = function () {
    console.log(this.name + "购买");
  };
  Test.prototype.placeOrder = function () {
    console.log(this.name + "下单购买");
  };
  Test =LoggerSonClass
  return Test;
}());

慕课网 TS 高级课程

10-5 【泛型工厂类继承装饰器】 泛型工厂类继承装饰器实现和意义

课程安排:1. 泛型工厂类继承装饰器实现 2. 泛型工厂类继承装饰器意义

// 1. 泛型工厂类继承装饰器
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");

慕课网 TS 高级课程

10-6 【泛型工厂类继承装饰器底层源码】逐行深剖+优化底层 JS 源码

课程安排 1. 拷贝第五章继承源码代码 2. 底层 JS 组合装饰器和目标类 __decorate 函数

//  1.  拷贝第五章继承源码代码
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;
}
// 底层 JS 组合装饰器和目标类 __decorate 函数结束

// 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");

慕课网 TS 高级课程

10-7 【泛型工厂类匿名类+继承装饰器】匿名类在装饰器中的实现

课程安排:匿名类在装饰器中的实现

function ClassFunctionExtends
  <T extends { new(...args: any[]): any }>
  (mytargetClass: T) {
  console.log("mytargetClass", mytargetClass);
  return class extends mytargetClass {
    constructor(...args: any[]) {
      super(args);
      console.log("SonClass执行结束");
    }
    commonMethod() {
      //console.log("this:", this)
      console.log("name:", this.name)
    }
  }
}
@ClassFunctionExtends
class Test {
  name!: string;
  age!: number
  // 1.先执行原来构造函数
  constructor(name: string) {
    this.name = name;
    console.log("执行");
    //console.log("beforendame:", this.name)// lisi
  }
  eat() {
    console.log(this.name, "吃饭");
  }
}

慕课网 TS 高级课程

10-8 【方法装饰器】 拦截器意义,如何实现前置丶后置拦截器功能?【真实应用场景】

10-8 课程安排:1. 方法装饰器的实现 2. 方法装饰器拦截器意义 3. 拦截器的前置、后置功能实现

1. 方法装饰器的实现

// 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. 方法拦截器意义

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

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

3. 拦截器的前置、后置功能实现

增强目标类的方法功能
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:");
  }
}

慕课网 TS 高级课程

10-9 【方法装饰器底层源码】逐行深剖底层 JS 源码

课程安排:逐行深剖底层 JS 源码

"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:");
  };
}

慕课网 TS 高级课程

10-10 【方法装饰器底层源码 关联的 BUG 解决】方法拦截功能功能为什么会失效?

课程安排:

  1. 测试删除__decorate方法最后一句代码 【Object.defineProperty(target, key, targetinfo) 】结果

  2. 增加 Object.defineProperty(target, key, targetinfo) 代码的意义

"use strict";
// __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;
}

慕课网 TS 高级课程

10-11 【属性装饰器】属性装饰器应用——顾客等级说明实现 1
// 属性装饰器【这里先学会使用,后面实战再深入】
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()
10-11 【属性装饰器底层源码】 逐行解析底层JS源码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();

10-12 【属性丶类丶方法装饰器综合应用】依赖注入+请求方法,控制器初步实现

//  Inject 依赖注入装饰器【这里先学会属性、类装饰器组合 连起来即可,为后面深入铺点基础,暂时不讲解依赖注入,对于依赖注入的好处,什么是 Controller, Service 层,后面实战再深入】

// 1 集合类
class Collection<T = any> {
  static collection: Collection = new Collection();

  private constructor() { }

  private containerMap = new Map<string | symbol, any>();

  public set(id: string | symbol, value: T): void {
    this.containerMap.set(id, value);
  }

  public get(id: string | symbol): T {
    return this.containerMap.get(id);
  }

  public has(id: string | symbol): Boolean {
    return this.containerMap.has(id);
  }
}
export default Collection.collection;

// 2 编写业务类
export class UserService {
  pname: string = "人民"
  public login() {
    console.log(this.pname + "登录....");
  }
}

//  3 编写类【控制器类———这个环节为初步实现,后面实战再扩充】
import { Inject } from './injectdecortator'
import { PeopleService } from './UserService'
import CollectionInstance from './Collection'
import ControllerDecorator from './ControllerDecorator'
import MethodDecorator from './methoddecorator'
import { Inject } from './injectdecortator'
import { UserService } from './UserService'
import CollectionInstance from './Collection'
import Controller from './ControllerDecorator'
import { RequestMethodDecorator } from './methoddecorator'
// 装饰器执行顺序: 1.属性装饰器==>2.方法参数装饰器==>3.方法装饰器===>4.类装饰器
@Controller("/")
class UserController {

  @Inject("userService")
  private userService?: UserService

  @RequestMethodDecorator("/login")
  public login(): void {
    //let peopleServiceInstace = CollectionInstance.get("userService");
    //peopleServiceInstace.login();
  }
}
//let controller = new Controller();
//controller.login();
export { }

// 4. 编写属性装饰器 【依赖注入属性装饰器———这个环节为初步实现,后面实战再深入】
//  安装元数据的第三方包 [yarn add reflect-metadata -S]
//  使用元数据装饰器需要安装元数据的第三方包【reflect-metadata】,这里先提前用下,后面会详解
// 元数据--为了帮助类,方法,属性实现一定的功能,而附加在其上的一些数据
// 分类: 自定义的元数据  内置元数据
import 'reflect-metadata'
import CollectionInstance from './Collection'
type MyPropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
export function Inject(injectid?: string): MyPropertyDecorator {
  return (targetClassPrototype, propertyKey) => {

    console.log("进入注入属性装饰器...Inject....")
    console.log("target:", targetClassPrototype);
    console.log("propertyKey:", propertyKey)
    console.log("依赖注入的id为:", injectid);

    // 重要
    const InjectServiceClass = Reflect.getMetadata("design:type", targetClassPrototype, propertyKey);
    CollectionInstance.set("userService", new InjectServiceClass())

    console.log("InjectServiceClass.prototype:", InjectServiceClass.prototype)
  }
}

// 5 编写方法装饰器【请求方法装饰器———这个环节为初步实现,后面实战再深入】
import 'reflect-metadata'
export default function get(path: string): MethodDecorator {
  return function (classTargetPrototype, methodname, dataprops) {
    console.log("进入到方法饰器", "path:", path);
    Reflect.defineMetadata('path', path, classTargetPrototype, methodname)
  }
}


// 6 编写类装饰器【控制器装饰器——这个环节为初步实现,后面实战再深入】
import 'reflect-metadata'
export default function ControllerDecorator<T extends { new(...args: any): any }>
  (targetClass: T) {
  console.log("进入到类控制器装饰器");
  Object.keys(targetClass.prototype).forEach((methodname) => {
    let path = Reflect.getMetadata("path", targetClass.prototype, methodname)
    console.log("path:", path);
  })
}

export { }