资讯专栏INFORMATION COLUMN

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

Barry_Ng / 3295人阅读

摘要:可见参数装饰器函数需要个参数被装饰类的原型,装饰参数所属的方法名,参数的索引。参数装饰器不应当用来修改构造器方法或属性的行为,它只应当用来产生某种元数据。一旦元数据被创建,我们便可以用其它的装饰器去读取它。

之前已经分别介绍了方法装饰器、属性装饰器和类装饰器,这篇文章我们来继续关注这些话题:

参数装饰器

装饰器工厂

我们将围绕以下这个例子,来探讨这些概念:

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101516.html

相关文章

  • 装饰与元数据反射(1)方法装饰

    摘要:使用装饰器的方法很简单在装饰器名前加字符,写在想要装饰的方法上,类似写注释的方式装饰器实际上是一个函数,入参为所装饰的方法,返回值为装饰后的方法。经过装饰过的方法,它依然按照原来的方式执行,只是额外执行了附件的装饰器函数的功能。 让我来深入地了解一下TypeScript对于装饰器模式的实现,以及反射与依赖注入等相关特性。 在Typescript的源代码中,可以看到装饰器能用来修饰cla...

    xiaochao 评论0 收藏0
  • 装饰与元数据反射(4)元数据反射

    摘要:庆幸的是,已经支持反射机制,来看看这个特性吧元数据反射可以通过安装包来使用元数据反射的若要使用它,我们需要在中设置为,同时添加的引用,同时加载文件。复杂类型序列化的团队为复杂类型的元数据序列化做出了努力。 本篇内容包括如下部分: 为什么JavaScript中需要反射 元数据反射API 基本类型序列化 复杂类型序列化 为什么JavaScript中需要反射? 关于反射的概念,摘自百度百...

    gaosboy 评论0 收藏0
  • 装饰与元数据反射(2)属与类性装饰

    摘要:值得注意的是,的返回值复写了原始的构造函数,原因是类装饰器必须返回一个构造器函数。原始构造函数的原型被复制给的原型,以确保在创建一个的新实例时,操作符如愿以偿,具体原因可参考鄙人另一篇文章原型与对象。 上一篇文章中,我们讨论了TypeScript源码中关于方法装饰器的实现,搞明白了如下几个问题: 装饰器函数是如何被调用的? 装饰器函数参数是如何传入的? __decorate函数干了...

    Shisui 评论0 收藏0
  • 流畅的 Python - 5. 装饰闭包

    摘要:看了这一章,发现原来是装饰器,又一新知识。期间,装饰器会做一些额外的工作。书中介绍了模块中的三个装饰器。另一个是,这个装饰器把函数结果保存了起来,避免传入相同参数时重复计算。叠放不奇怪,装饰器返回的就是函数或可调用对象。 在 Web 框架 Flask 中,最常看到的或许是以@app.route开头的那行代码。由于还是刚接触 Flask,所以对这种语法还不熟悉。看了这一章,发现原来是装饰...

    Markxu 评论0 收藏0
  • 聊聊Typescript中的设计模式——装饰篇(decorators)

    摘要:本文从装饰模式出发,聊聊中的装饰器和注解。该函数的函数名。不提供元数据的支持。中的元数据操作可以通过包来实现对于元数据的操作。   随着Typescript的普及,在KOA2和nestjs等nodejs框架中经常看到类似于java spring中注解的写法。本文从装饰模式出发,聊聊Typescipt中的装饰器和注解。 什么是装饰者模式 Typescript中的装饰器 Typescr...

    yiliang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<