本篇内容包含以下部份:
- 为何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[];
这类信息能够用来在运行时考证一个实例是不是完成了特定的接口,而这个功用关于一个依靠翻转容器迥殊的有效。