博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
装饰器与元数据反射(3)参数装饰器
阅读量:5881 次
发布时间:2019-06-19

本文共 3007 字,大约阅读时间需要 10 分钟。

之前已经分别介绍了、,这篇文章我们来继续关注这些话题:

  • 参数装饰器
  • 装饰器工厂

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

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装饰器建议定义一个如下的装饰器工厂:

装饰器工厂首先是一个函数,它接受任意数量的参数,同时返回如前所述的四种之一特定类型的装饰器。

虽然已经讨论四种装饰是如何实现及使用的,但还是有一些可以改进的地方,观察下面的代码片段:

@logClassclass 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;   }}

这里装饰器的使用是没问题的,但如果我们可以不关心装饰器的类型,而在任何地方使用岂不方便,就像下面的样子:

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

转载地址:http://bwvix.baihongyu.com/

你可能感兴趣的文章
数据库之数据排序
查看>>
struts2将数据通过Json格式显示于EasyUI-datagrid数据表格
查看>>
牛客21天刷题_day#3
查看>>
Appium-We wanted {"required":["value"]} and you sent ["text","sessionId","id","value"]
查看>>
Classification Truth Table
查看>>
JVM学习:对象的创建和内存分配
查看>>
JavaScript基础精讲
查看>>
C++ 静态变量 全局变量 const
查看>>
vs 高级保存选项的设置
查看>>
Java读取文本指定的某一行内容的方法
查看>>
软件工程敏捷开发04
查看>>
Practise Site Home Sample Page Codes de carte cadeau Amazon | Codes Promo Amazon
查看>>
linux c下输入密码不回显
查看>>
浏览器全屏与退出
查看>>
js判断浏览器类型
查看>>
Java中的NIO基础知识
查看>>
20145223《信息安全系统设计基础》第3周学习总结
查看>>
横屏设置坐标
查看>>
SpringCache学习实践
查看>>
使用getline输入一行字符串
查看>>