JavaScript Reflect Metadata实现详解(2)

class A { @Reflect.metadata('name', 'hello') hello() {} } const t1 = new A() const t2 = new A() Reflect.defineMetadata('otherName', 'world', t2, 'hello') Reflect.getMetadata('name', t1, 'hello') // 'hello' Reflect.getMetadata('name', t2, 'hello') // 'hello' Reflect.getMetadata('otherName', t2, 'hello') // 'world' Reflect.getOwnMetadata('name', t2, 'hello') // undefined Reflect.getOwnMetadata('otherName', t2, 'hello') // 'world'

用途

其实所有的用途都是一个目的,给对象添加额外的信息,但是不影响对象的结构。这一点很重要,当你给对象添加了一个原信息的时候,对象是不会有任何的变化的,不会多 property,也不会有的 property 被修改了。
但是可以衍生出很多其他的用途。

Anuglar 中对特殊字段进行修饰 (Input),从而提升代码的可读性。

可以让装饰器拥有真正装饰对象而不改变对象的能力。让对象拥有更多语义上的功能。

API

namespace Reflect { // 用于装饰器 metadata(k, v): (target, property?) => void // 在对象上面定义元数据 defineMetadata(k, v, o, p?): void // 是否存在元数据 hasMetadata(k, o, p?): boolean hasOwnMetadata(k, o, p?): boolean // 获取元数据 getMetadata(k, o, p?): any getOwnMetadata(k, o, p?): any // 获取所有元数据的 Key getMetadataKeys(o, p?): any[] getOwnMetadataKeys(o, p?): any[] // 删除元数据 deleteMetadata(k, o, p?): boolean }

大家可能注意到,针对某些操作,会有 Own 的函数。这是因为有的操作是可以通过原型链进行操作的。这个后文讲解。

深入 Reflect Metadata

实现原理

如果你去翻看官网的文档,他会和你说,所有的元数据都是存在于对象下面的 [[Metadata]] 属性下面。一开始我也是这样认为的,新建一个 Symbol('Metadata'),然后将元数据放到这个 Symbol 对应的 Property 当中。直到我看了源码才发现并不是这样。请看例子

@Reflect.metadata('name', 'A') class A {} Object.getOwnPropertySymbols(A) // []

哈哈,并没有所谓的 Symbol,那么这些元数据都存在在哪里呢?

其实是内部的一个 WeakMap 中。他正是利用了 WeakMap 不增加引用计数的特点,将对象作为 Key,元数据集合作为 Value,存到 WeakMap 中去。

如果你认真探寻的话,你会发现其内部的数据结构其实是这样的

WeakMap<any, Map<any, Map<any, any>>>

是不是超级绕,但是我们从调用的角度来思考,这就一点都不绕了。

weakMap.get(o).get(p).get(k)

先根据对象获取,然后在根据属性,最后根据元数据的 Key 获取最终要的数据。

End

因为 Reflect Metadata 实在是比较简单,这里就不多讲解了。更多内容请查看Spec

题外话

其实看了源码之后还是挺惊讶的,按照一般的套路,很多 polyfill 会让你提供一些前置的 polyfill 之后,当前的 polyfill 才能使用。但是 reflect-metadata 竟然内部自己实现了很多的 polyfill 和算法。比如 Map, Set, WeakMap, UUID。最惊讶的莫过于 WeakMap 了。不是很仔细的阅读了一下,好像还是会增加引用计数。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/895d5aff0d080688b6e6f7edc7379acb.html