reflect metadata & dependency injection

本系列文章是本人学习相关知识时所积累的笔记,以记录自己的学习历程,也为了方便回顾知识;故文章内容较为随意简练,抱着学习目的来的同学务必转移他处,以免我误人子弟~

Decorators & metadata reflection in TypeScript: From Novice to Expert (Part I)
从 JavaScript 到 TypeScript 4 - 装饰器和反射

关于装饰器函数需不需要返回值的问题

declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;

declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;

declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;

declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => void;

  • 类装饰器接收1个参数(当前类的构造函数F),通过修改该构造函数并返回可达到修改类的效果(也可不返回)
  • 属性装饰器接收2个参数(当前类的原型对象和属性名),无需返回值

    静态属性接收当前 类的构造函数 和 属性名

  • 方法装饰器接收3个参数(当前类的原型对象、方法名和方法描述对象),通过修改方法描述对象并返回达到修改方法的效果(也可不返回)

    静态方法接收当前 类的构造函数 、属性名 和 方法描述对象

  • 参数装饰器接收3个参数(当前类的原型对象、方法名和参数所在方法的索引),无需返回值

查看 MethodDecorator 的ts解析代码

ts代码:

class C {
    @log
    foo(n: number) {
        return n * 2;
    }
}

function log(target: Object, key: string, descriptor: any) {
    ...
}

解析后的js代码:

var C = (function () {
    function C() {
    }
    C.prototype.foo = function (n) {
        return n * 2;
    };
    Object.defineProperty(C.prototype, "foo",
        __decorate(
	        [log], 
	        C.prototype, 
	        "foo", 
	        Object.getOwnPropertyDescriptor(C.prototype, "foo")
        ));
    return C;
})();

var __decorate = this.__decorate || function (decorators, target, key, desc) {
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
    return Reflect.decorate(decorators, target, key, desc);
  }
  switch (arguments.length) {
    case 2: 
      return decorators.reduceRight(function(o, d) { 
        return (d && d(o)) || o; 
      }, target);
    case 3: 
      return decorators.reduceRight(function(o, d) { 
        return (d && d(target, key)), void 0; 
      }, void 0);
    case 4: 
      return decorators.reduceRight(function(o, d) { 
        return (d && d(target, key, o)) || o; 
      }, desc);
  }
};

为什么是 reduceRight
因为typescript的装饰器是 先进后出 的原则;假设有3个方法装饰器 a1 a2 a3,则 decorators 为[a1 ,a2 ,a3],a1最先进入,a3最后进入;但当方法被调用时,装饰器的执行顺序是从右到左,即a3 a2 a1 的顺序

如果是 MethodDecorator ,即 case 4,则 __decorate最终会返回一个方法描述对象,如果某个方法装饰器返回方法描述对象desc,则 __decorate返回desc,否则放回方法原生的方法描述对象;先执行的装饰器所返回的方法描述对象会被后面返回的取代

Tips

Typescript 中,Reflect.getMetadata 的使用有很多规矩需要注意:

import "reflect-metadata"

type Constructor<T = any> = new (...args: any[]) => T;

const ClassDec = (): ClassDecorator => {
  return (target) => {
      return;
  }
};
const PropertyDec = (): PropertyDecorator => {
  return (target, key) => {
      return;
  }
};
const MethodDec = (): MethodDecorator => {
  return (target, key, desc) => {
      return;
  }
};

class OtherService {
  constructor(){}
  a=0;
}
@ClassDec()
class TestService {
  constructor(public readonly otherService: OtherService) { };
  @PropertyDec()
  public name: string;
  @MethodDec()
  public handle(event: number) {
      console.log(this.otherService.a);
   }
}

const Factory = <T>(target: Constructor<T>): T => {
  // 获取所有注入的服务
  console.log(Reflect.getMetadata('design:paramtypes', target));
  console.log(Reflect.getMetadata('design:type', target))
  console.log(Reflect.getMetadata('design:returntype', target));

  console.log(Reflect.getMetadata('design:paramtypes', target.prototype, "handle"));
  console.log(Reflect.getMetadata('design:type', target.prototype, "handle"))
  console.log(Reflect.getMetadata('design:returntype', target.prototype, "handle"));

  console.log(Reflect.getMetadata('design:paramtypes', target.prototype, "name"))
  console.log(Reflect.getMetadata('design:returntype', target.prototype, "name"));
  console.log(Reflect.getMetadata('design:type', target.prototype, "name"))
  return new target();
};

Factory(TestService);


  1. 类不支持提升,OtherService 必须在使用前声明(写在TestService 前面),否则 Reflect.getMetadata('design:paramtypes', target) 不起作用

    尝试将OtherService放到TestService后面,观察运行结果

  2. 要想在非装饰器函数中取得元数据,需要对应的类(或属性或方法)有一个装饰器(此装饰器可以不起其他作用)

    尝试注释掉 @ClassDec(), @PropertyDec(), @MethodDec(),观察运行结果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章