装潢器与元数据反射(2)属与类性装潢器

上一篇文章中,我们议论了TypeScript源码中关于要领装潢器的完成,搞邃晓了以下几个问题:

  • 装潢器函数是怎样被挪用的?
  • 装潢器函数参数是怎样传入的?
  • __decorate函数干了些什么事情?

接下来我们继承属性装潢器的视察。

属性装潢器

属性装潢器的声明标识以下:

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

以下我们为一个类的属性增加了一个名为@logProperty的装潢器

class Person { 

  @logProperty
  public name: string;
  public surname: string;

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

上一篇诠释过,当这段代码末了被编译成JavaScript执行时,要领__decorate会被挪用,但此处会少末了一个参数(经由历程Object. getOwnPropertyDescriptor属性描述符)

var Person = (function () {
    function Person(name, surname) {
        this.name = name;
        this.surname = surname;
    }
    __decorate(
      [logProperty],
      Person.prototype,
      "name"
    );
    return Person;
})();

须要注重的是,此次TypeScript编译器并没像要领装潢器那样,运用__decorate返回的效果掩盖原始属性。缘由是属性装潢器并不须要返回什么。

Object.defineProperty(C.prototype, "foo",
    __decorate(
      [log],
      C.prototype,
      "foo",
      Object.getOwnPropertyDescriptor(C.prototype, "foo")
    )
);

那末,接下来详细完成这个@logProperty装潢器

function logProperty(target: any, key: string) {

  // 属性值
  var _val = this[key];

  // getter
  var getter = function () {
    console.log(`Get: ${key} => ${_val}`);
    return _val;
  };

  // setter
  var setter = function (newVal) {
    console.log(`Set: ${key} => ${newVal}`);
    _val = newVal;
  };

  // 删除属性
  if (delete this[key]) {
    // 建立新的属性
    Object.defineProperty(target, key, {
      get: getter,
      set: setter,
      enumerable: true,
      configurable: true
    });
  }
}

完成历程起首声清楚明了一个变量_val,并用所装潢的属性值给它赋值(此处的this指向类的原型,key为属性的名字)。

接着声清楚明了两个要领gettersetter,因为函数是闭包建立的,所以在个中能够接见变量_val,在个中能够增加分外的自定义行动,这里增加了将属性值打印在控制台的操纵。

然后运用delete操纵符将原属性从类的原型中删除,不过须要注重的是:假如属性存在不可设置的属性时,这里if(delete this[key])会返回false。而当属性被胜利删除,要领Object.defineProperty()将建立一个和原属性同名的属性,差别的是新的属性gettersetter要领,运用上面新建立的。

至此,属性装潢器的完成就完成了,运转效果以下:

var me = new Person("Remo", "Jansen");  
// Set: name => Remo

me.name = "Remo H.";                       
// Set: name => Remo H.

name;
// Get: name Remo H.

类装潢器

类装潢器的声明标识以下:

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

能够像以下体式格局运用类装潢器:

@logClass
class Person { 

  public name: string;
  public surname: string;

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

和之前差别的是,经由TypeScript编译器编译为JavaScript后,挪用__decorate函数时,与要领装潢器比拟少了后两个参数。仅传递了Person而非Person.prototype

var Person = (function () {
    function Person(name, surname) {
        this.name = name;
        this.surname = surname;
    }
    Person = __decorate(
      [logClass],
      Person
    );
    return Person;
})();

值得注重的是,__decorate的返回值复写了原始的组织函数,缘由是类装潢器必需返回一个组织器函数。接下来我们就来完成上面用到的类装潢器@logClass

function logClass(target: any) {

  // 保留对原始组织函数的援用
  var original = target;

  // 用来天生类实例的要领
  function construct(constructor, args) {
    var c : any = function () {
      return constructor.apply(this, args);
    }
    c.prototype = constructor.prototype;
    return new c();
  }

  // 新的组织函数
  var f : any = function (...args) {
    console.log("New: " + original.name); 
    return construct(original, args);
  }

  // 复制原型以便`intanceof`操纵符能够运用
  f.prototype = original.prototype;

  // 返回新的组织函数(会掩盖原有组织函数)
  return f;
}

这里完成的组织器中,声清楚明了一个名为original的变量,并将所装潢类的组织函数赋值给它。接着声明一个东西函数construct,用来建立类的实例。然后定义新的组织函数f,在个中挪用本来的组织函数并将初始化的类名打印在控制台,固然我们也能够增加一些其他自定义的行动。

原始组织函数的原型被复制给f的原型,以确保在建立一个Person的新实例时,instanceof操纵符如愿以偿,详细缘由可参考鄙人另一篇文章原型与对象

至此类装潢器的完成就完成了,能够考证下:

var me = new Person("Remo", "Jansen");  
// New: Person

me instanceof Person; 
// true
    原文作者:sept08
    原文地址: https://segmentfault.com/a/1190000018091354
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞