装潢器与元数据反射(3)参数装潢器

之前已离别引见了要领装潢器属性装潢器和类装潢器,这篇文章我们来继承关注这些话题:

  • 参数装潢器
  • 装潢器工场

我们将缭绕以下这个例子,来议论这些观点:

class Person { 

  public name: string;
  public surname: string;

  constructor(name : string, surname : string) { 
    this.name = name;
    this.surname = surname;
  }

  public saySomething(something : string) : string { 
    return this.name + " " + this.surname + " says: " + something; 
  }
}

参数装潢器

TypeScript关于参数装潢器的声明以下

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

以下我们为类PersonsaySomething要领的参数增添一个参数装潢器

public saySomething(@logParameter something : string) : string { 
    return this.name + " " + this.surname + " says: " + something; 
}

终究被编译为JavaScript的模样为:

Object.defineProperty(Person.prototype, "saySomething",
    __decorate(
        [__param(0, logParameter)],
        Person.prototype,
        "saySomething",
        Object.getOwnPropertyDescriptor(Person.prototype, "saySomething")
    )
);
return Person;

假如将其和之前的装潢器比较,是不是会发明又运用了Object.defineProperty()要领,那末是不是意味着saySomething将被__decorated函数的返回值替代?

我们发明这里有个新函数__param,TypeScript编译器天生以下:

var __param = this.__param || function (index, decorator) {
    // 返回一个装潢器函数
    return function (target, key) {
        // 运用装潢器(疏忽返回值)
        decorator(target, key, index); 
    }
};

如上所示,挪用参数装潢器,其并没有返回值,这就意味着,函数__decorate的挪用返回并没有掩盖要领saySomething,也很好明白:参数装潢器要毛返回。

可见参数装潢器函数须要3个参数:被装潢类的原型,装潢参数所属的要领名,参数的索引。详细的完成以下:

function logParameter(target: any, key : string, index : number) {
  var metadataKey = `log_${key}_parameters`;
  if (Array.isArray(target[metadataKey])) {
    target[metadataKey].push(index);
  }
  else { 
    target[metadataKey] = [index];
  }
}

个中向类的原型中增添一个新的属性metadataKey,该属性值是一个数组,包括所装潢参数的索引,能够把它看成元数据。

参数装潢器不该当用来修正组织器、要领或属性的行动,它只应当用来发生某种元数据。一旦元数据被建立,我们便能够用别的的装潢器去读取它。

装潢器工场

官方TypeScript装潢器发起定义一个以下的装潢器工场:

装潢器工场首先是一个函数,它接收恣意数目的参数,同时返回如前所述的四种之一特定范例的装潢器。

虽然已议论四种装潢是怎样完成及运用的,但照样有一些能够革新的处所,视察下面的代码片断:

@logClass
class Person { 

  @logProperty
  public name: string;

  public surname: string;

  constructor(name : string, surname : string) { 
    this.name = name;
    this.surname = surname;
  }

  @logMethod
  public saySomething(@logParameter something : string) : string { 
    return this.name + " " + this.surname + " says: " + something; 
  }
}

这里装潢器的运用是没问题的,但假如我们能够不关心装潢器的范例,而在任何处所运用岂不轻易,就像下面的模样:

@log
class Person { 

  @log
  public name: string;

  public surname: string;

  constructor(name : string, surname : string) { 
    this.name = name;
    this.surname = surname;
  }

  @log
  public saySomething(@log something : string) : string { 
    return this.name + " " + this.surname + " says: " + something; 
  }
}

这边是装潢器工场的运用诉求,它能够辨认详细情况下该运用哪一种范例的装潢器,荣幸的是,我们能够经由过程传递给装潢器的参数来辨别它的范例。

function log(...args : any[]) {
  switch(args.length) {
    case 1:
      return logClass.apply(this, args);
    case 2:
      return logProperty.apply(this, args);
    case 3:
      if(typeof args[2] === "number") {
        return logParameter.apply(this, args);
      }
      return logMethod.apply(this, args);
    default:
      throw new Error("Decorators are not valid here!");
  }
}
    原文作者:sept08
    原文地址: https://segmentfault.com/a/1190000018092722
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞