资讯专栏INFORMATION COLUMN

装饰器与元数据反射(4)元数据反射

gaosboy / 462人阅读

摘要:庆幸的是,已经支持反射机制,来看看这个特性吧元数据反射可以通过安装包来使用元数据反射的若要使用它,我们需要在中设置为,同时添加的引用,同时加载文件。复杂类型序列化的团队为复杂类型的元数据序列化做出了努力。

本篇内容包括如下部分:

为什么JavaScript中需要反射

元数据反射API

基本类型序列化

复杂类型序列化

为什么JavaScript中需要反射?

关于反射的概念,摘自百度百科

在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

可见反射机制对于依赖注入、运行时类型断言、测试是非常有用的,同时随着基于JavaScript的应用做的越来越大,使得我们希望有一些工具和特性可以用来应对增长的复杂度,例如控制反转,运行时类型断言等。但由于JavaScript语言中没有反射机制,所以导致这些东西要么没法实现,要么实现的不如C#Java语言实现的强大。

强大的反射API允许我们可以在运行时测试一个未知的类,以及找到关于它的任何信息,包括:名称、类型、接口等。虽然可以使用诸如Object.getOwnPropertyDescriptor()Object.keys()查询到一些信息,但我们需要反射来实现更强大的开发工具。庆幸的是,TypeScript已经支持反射机制,来看看这个特性吧

元数据反射API

可以通过安装reflect-metadata包来使用元数据反射的API

npm install reflect-metadata;

若要使用它,我们需要在tsconfig.json中设置emitDecoratorMetadatatrue,同时添加reflect-metadata.d.ts的引用,同时加载Reflect.js文件。然后我们来实现装饰器并使用反射元数据设计的键值,目前可用的有:

类型元数据:design:type

参数类型元数据:design:paramtypes

返回类型元数据:design:returntype

我们来通过一组例子来说明

1)获取类型元数据

首先声明如下的属性装饰器:

function logType(target : any, key : string) {
    var t = Reflect.getMetadata("design:type", target, key);
    console.log(`${key} type: ${t.name}`);
}

接下来将其应用到一个类的属性上,以获取其类型:

class Demo{ 
    @logType
    public attr1 : string;
}

这个例子将会在控制台中打印如下信息:

attr1 type: String
2) 获取参数类型元数据

声明参数装饰器如下:

function logParamTypes(target : any, key : string) {
    var types = Reflect.getMetadata("design:paramtypes", target, key);
    var s = types.map(a => a.name).join();
    console.log(`${key} param types: ${s}`);
} 

然后将它应用在一个类方法的参数上,用以获取所装饰参数的类型:

class Foo {}
interface IFoo {}

class Demo{ 
    @logParameters
        param1 : string,
        param2 : number,
        param3 : Foo,
        param4 : { test : string },
        param5 : IFoo,
        param6 : Function,
        param7 : (a : number) => void,
    ) : number { 
        return 1
    }
}

这个例子的执行结果是:

doSomething param types: String, Number, Foo, Object, Object, Function, Function
3) 获取返回类型元数据

同样的我们可以使用"design:returntype"元数据键值,来获取一个方法的返回类型:

Reflect.getMetadata("design:returntype", target, key);
基本类型序列化

让我们回看上面关于"design:paramtypes"的例子,注意到接口IFoo和对象字面量{test: string}被序列化为Object,这是因为TypeScript仅支持基本类型的序列化,基本类型序列化规则如下:

number序列化为Number

string序列化为String

boolean序列化为Boolean

any序列化为Object

void序列化为undefined

Array序列化为Array

元组Tuple序列化为Array

class序列化为类的构造函数

枚举Enum序列化为Number

剩下的所有其他类型都被序列化为Object

接口和对象字面量可能在之后的复杂类型序列化中会被做具体的处理。

复杂类型序列化

TypeScript的团队为复杂类型的元数据序列化做出了努力。上面列出的序列化规则对基本类型依然适用,但对复杂类型提出了不同的序列化逻辑。如下是通过一个例子来描述所有可能的类型:

interface _Type {
  /** 
    * Describes the specific shape of the type.
    * @remarks 
    * One of: "typeparameter", "typereference", "interface", "tuple", "union" or "function".
    */
  kind: string; 
}

我们也可以找到用于描述每种可能类型的类,例如用于序列化通用接口interface foo

// 描述一个通用接口
interface InterfaceType extends _Type {
  kind: string; // "interface"

  // 通用类型参数. 可能为undefined.
  typeParameters?: TypeParameter[];

  // 实现的接口.
  implements?: Type[];

  // 类型的成员 可能为undefined.
  members?: { [key: string | symbol | number]: Type; };

  // 类型的调用标识. 可能为undefined.
  call?: Signature[];

  // 类型的构造标识. 可能为undefined.
  construct?: Signature[];

  // 类型的索引标识. 可能为undefined.
  index?: Signature[];
}

这里有一个属性指出实现了哪些接口

// 实现的接口
implements?: Type[];

这种信息可以用来在运行时验证一个实例是否实现了特定的接口,而这个功能对于一个依赖翻转容器特别的有用。

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

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

相关文章

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

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

    xiaochao 评论0 收藏0
  • 装饰数据反射(3)参数装饰

    摘要:可见参数装饰器函数需要个参数被装饰类的原型,装饰参数所属的方法名,参数的索引。参数装饰器不应当用来修改构造器方法或属性的行为,它只应当用来产生某种元数据。一旦元数据被创建,我们便可以用其它的装饰器去读取它。 之前已经分别介绍了方法装饰器、属性装饰器和类装饰器,这篇文章我们来继续关注这些话题: 参数装饰器 装饰器工厂 我们将围绕以下这个例子,来探讨这些概念: class Person...

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

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

    Shisui 评论0 收藏0
  • 第12章 编程与注解、反射 《Kotlin 项目实战开发》

    摘要:第章元编程与注解反射反射是在运行时获取类的函数方法属性父类接口注解元数据泛型信息等类的内部信息的机制。本章介绍中的注解与反射编程的相关内容。元编程本质上是一种对源代码本身进行高层次抽象的编码技术。反射是促进元编程的一种很有价值的语言特性。 第12章 元编程与注解、反射 反射(Reflection)是在运行时获取类的函数(方法)、属性、父类、接口、注解元数据、泛型信息等类的内部信息的机...

    joyqi 评论0 收藏0
  • EMF学习笔记(二)——使用EMF编程——开发数据

    摘要:使用元数据包中包含了中每一个被建模类对应的接口。任何对象的元数据是使用的实现来表示的。加载模型的序列化形式是个在运行期间获取元数据的有效方法。反射提供一个反射式,可以检查对象的元数据以及一般地访问和操纵数据。 使用元数据   Java包org.eclipse.emf.ecore中包含了Ecore中每一个被建模类对应的接口。任何EMF对象的元数据是使用Ecore的实现(implement...

    Jiavan 评论0 收藏0

发表评论

0条评论

gaosboy

|高级讲师

TA的文章

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