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

栏目: JavaScript · 发布时间: 6年前

内容简介:上一篇文章中,我们讨论了TypeScript源码中关于方法装饰器的实现,搞明白了如下几个问题:接下来我们继续属性装饰器的声明标识如下:

上一篇文章中,我们讨论了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

以上所述就是小编给大家介绍的《装饰器与元数据反射(2)属与类性装饰器》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Two Scoops of Django

Two Scoops of Django

Daniel Greenfeld、Audrey M. Roy / CreateSpace Independent Publishing Platform / 2013-4-16 / USD 29.95

Two Scoops of Django: Best Practices For Django 1.5 is chock-full of material that will help you with your Django projects. We'll introduce you to various tips, tricks, patterns, code snippets, and......一起来看看 《Two Scoops of Django》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具