装饰器与元数据反射(4)元数据反射
栏目: JavaScript · 发布时间: 6年前
内容简介:本篇内容包括如下部分:关于反射的概念,摘自在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
本篇内容包括如下部分:
- 为什么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
中设置 emitDecoratorMetadata
为 true
,同时添加 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<bar>
:
// 描述一个通用接口
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[];
这种信息可以用来在运行时验证一个实例是否实现了特定的接口,而这个功能对于一个依赖翻转容器特别的有用。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)
- 仅反射加载(ReflectionOnlyLoadFrom)的 .NET 程序集,如何反射获取它的 Attribute 元数据呢?
- 装饰器与元数据反射(1)方法装饰器
- Memcache UDP 反射放大攻击 II: 最近的数据分析
- 装饰器与元数据反射(2)属与类性装饰器
- Go语言反射之反射调用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Programming Amazon Web Services
James Murty / O'Reilly Media / 2008-3-25 / USD 49.99
Building on the success of its storefront and fulfillment services, Amazon now allows businesses to "rent" computing power, data storage and bandwidth on its vast network platform. This book demonstra......一起来看看 《Programming Amazon Web Services》 这本书的介绍吧!