阅读本节内容之前,请先阅读 decorator 有关内容:
ECMAScript 2015 增加了 Reflect 对象。Reflect 提供了一些非常有用的功能。
Reflect.ownKeys
:
// This object will have prop = "cool"
class RootObject {
public prop: string = "cool";
}
// Its prototype will have foo = "bar"
RootObject.prototype = { foo: "bar" } as any;
// Create an instance
const root = new RootObject();
// for...in moves up the prototype chain
// tslint:disable-next-line:forin
for (const key in root) {
console.log(key);
}
// prop
// foo
// hasOwnProperty will prevent this
// but requires an extra conditional
for (const key in root) {
if (root.hasOwnProperty(key)) {
console.log(key);
}
}
// prop
// Reflect.ownKeys solves it in one line
for (const key of Reflect.ownKeys(root)) {
console.log(key);
}
// prop
Reflect.has
// The visibility compiles out but whatever
class Demo {
public foo: number = 1;
protected bar: number = 2;
private baz: number = 3;
}
// Create an instance
const demo = new Demo();
console.log(Reflect.has(demo, "foo"));
// true
console.log(Reflect.has(demo, "bar"));
// true
console.log(Reflect.has(demo, "baz"));
// true
console.log(Reflect.has(demo, "qqq"));
// false
Reflect.deleteProperty
:
(() => {
"use strict";
const sampleDeleteObject = {
one: 1,
three: 3,
two: 2,
};
// Delete a property with delete
console.log(delete sampleDeleteObject.one);
// true
// Delete a property with Reflect
console.log(Reflect.deleteProperty(sampleDeleteObject, "two"));
// true
console.log(sampleDeleteObject);
// { three: 3 }
// Accidentally try to delete an object
try {
// tslint:disable-next-line:no-eval
console.log(eval("delete sampleDeleteObject"));
} catch (error) {
// do nothing
}
// Accidentally try to delete an object
console.log(Reflect.deleteProperty(sampleDeleteObject));
// true
console.log(sampleDeleteObject);
// { three: 3 }
})();
emitDecoratorMetadata
#typescript 提供了一些实验中的 reflection 功能。使用这些功能,必须开启相关设置:
{
"compilerOptions": {
"target": "ESNext",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": ["./**/*.ts"]
}
编写如下 ts 文件,并编译:
function LogMethod(
target: any,
propertyKey: string | symbol,
descriptor: PropertyDescriptor,
) {
console.log(target);
console.log(propertyKey);
console.log(descriptor);
}
class Demo {
@LogMethod
public foo(bar: number) {
// do nothing
}
}
const demo = new Demo();
编译结果:
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
function LogMethod(target, propertyKey, descriptor) {
console.log(target);
console.log(propertyKey);
console.log(descriptor);
}
class Demo {
foo(bar) {
// do nothing
}
}
__decorate([
LogMethod,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Number]),
__metadata("design:returntype", void 0)
], Demo.prototype, "foo", null);
const demo = new Demo();
开启了 emitDecoratorMetadata
后,编译的文件中多了 __metadata
函数。
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
__metadata
函数接收 k
和 v
(key-value) 键值对给 Reflect.metadata
函数,Reflect.metadata
会存储这些信息,并留作后面使用。默认情况下,emitDecoratorMetadata
会暴露三个属性:
design:type
: 装饰目标的类型,这里是 Function
design:paramtypes
:装饰目标的参数类型(数组列表形式),这里是 [Number]
design:returnType
:装饰目标的返回类型,这里是 void 0
看到这里可能会有个疑惑,那就是 Reflect.metadata
是个啥?
reflect-metadata
#reflect-metadata
是一个 npm package,官方文档也推荐了该 package,该包扩展了 relfection 的能力,安装了 reflect-metadata
后, Reflect.metadata
就定义了。
下面展示了如何通过 reflect-metadata 存储和获取信息:
import "reflect-metadata";
class BasicUsage {
constructor() {
// Explicitly define some metadata
// key, value, target, propertyKey
Reflect.defineMetadata("foo1", "bar1", this, "baz");
}
// Define metadata via a decorator
// key, value
@Reflect.metadata("foo2", "bar2")
public baz() {
// do nothing
}
}
const demoBasicUsage = new BasicUsage();
// key, target, propertyKey
console.log(Reflect.getMetadata("foo1", basicUsageDemo, "baz"));
// bar1
console.log(Reflect.getMetadata("foo2", basicUsageDemo, "baz"));
// bar2
在 decorator 中获取 metadata:
import "reflect-metadata";
function LogMethod(
target: any,
propertyKey: string | symbol,
descriptor: PropertyDescriptor,
) {
// Checks the type of the decorated object
console.log(Reflect.getMetadata("design:type", target, propertyKey));
// [Function: Function]
// Checks the types of all params
console.log(Reflect.getMetadata("design:paramtypes", target, propertyKey));
// [[Function: Number]]
// Checks the return type
console.log(Reflect.getMetadata("design:returntype", target, propertyKey));
// undefined
}
class Demo {
@LogMethod
public foo(bar: number) {
// do nothing
}
}
const demo = new Demo();
(完)