当前位置 主页 > 服务器问题 > Linux/apache问题 >

    JavaScript Reflect Metadata实现详解

    栏目:Linux/apache问题 时间:2019-12-12 16:44

    引言

    在 ES6 的规范当中,就已经存在 Reflect API 了。简单来说这个 API 的作用就是可以实现对变量操作的函数化,也就是反射。具体的关于这个 API 的内容,可以查看这个教程

    然而我们在这里讲到的,却是 Reflect 里面还没有的一个规范,那么就是 Reflect Metadata。

    Metadata

    想必对于其他语言的 Coder 来说,比如说 Java 或者 C#,Metadata 是很熟悉的。最简单的莫过于通过反射来获取类属性上面的批注(在 JS 当中,也就是所谓的装饰器)。从而可以更加优雅的对代码进行控制。

    而 JS 现在有装饰器,虽然现在还在 Stage2 阶段。但是 JS 的装饰器更多的是存在于对函数或者属性进行一些操作,比如修改他们的值,代理变量,自动绑定 this 等等功能。

    所以,后文当中我就使用 TypeScript 来进行讲解,因为 TypeScript 已经完整的实现了装饰器。

    虽然 Babel 也可以,但是需要各种配置,人懒,不想配置那么多。

    但是却无法实现通过反射来获取究竟有哪些装饰器添加到这个类/方法上。

    于是 Reflect Metadata 应运而生。

    Reflect Metadata

    Relfect Metadata,简单来说,你可以通过装饰器来给类添加一些自定义的信息。然后通过反射将这些信息提取出来。当然你也可以通过反射来添加这些信息。 就像是下面这个例子所示。

    @Reflect.metadata('name', 'A')
    class A {
     @Reflect.metadata('hello', 'world')
     public hello(): string {
      return 'hello world'
     }
    }
    
    Reflect.getMetadata('name', A) // 'A'
    Reflect.getMetadata('hello', new A()) // 'world'
    // 这里为什么要用 new A(),用 A 不行么?后文会讲到
    
    

    是不是很简单,那么我简单来介绍一下~

    概念

    首先,在这里有四个概念要区分一下:

    Metadata Key {Any} 后文简写 k。元数据的 Key,对于一个对象来说,他可以有很多元数据,每一个元数据都对应有一个 Key。一个很简单的例子就是说,你可以在一个对象上面设置一个叫做 'name' 的 Key 用来设置他的名字,用一个 'created time' 的 Key 来表示他创建的时间。这个 Key 可以是任意类型。在后面会讲到内部本质就是一个 Map 对象。 Metadata Value {Any} 后文简写 v。元数据的类型,任意类型都行。 Target {Object} 后文简写 o。表示要在这个对象上面添加元数据 Property {String|Symbol} 后文简写 p。用于设置在那个属性上面添加元数据。大家可能会想,这个是干什么用的,不是可以在对象上面添加元数据了么?其实不仅仅可以在对象上面添加元数据,甚至还可以在对象的属性上面添加元数据。其实大家可以这样理解,当你给一个对象定义元数据的时候,相当于你是默认指定了 undefined 作为 Property。 下面有一个例子大家可以看一下。

    大家明白了上面的概念之后,我之前给的那个例子就很简单了~不用我多说了。

    安装/使用

    下面不如正题,我们怎么开始使用 Reflect Metadata 呢?

    首先,你需要安装 reflect-metadata polyfill,然后引入之后就可以看到在 Reflect 对象下面有很多关于 Metadata 的函数了。因为这个还没有进入正式的协议,所以需要安装垫片使用。

    啥,Reflect 是啥,一个全局变量而已。

    你不需要担心这个垫片的质量,因为连 Angular 都在使用呢,你怕啥。

    之后你就可以安装我上面写的示例,在 TypeScript 当中去跑了。

    类/属性/方法 装饰器

    看这个例子。

    @Reflect.metadata('name', 'A')
    class A {
     @Reflect.metadata('name', 'hello')
     hello() {}
    }
    
    const objs = [A, new A, A.prototype]
    const res = objs.map(obj => [
     Reflect.getMetadata('name', obj),
     Reflect.getMetadata('name', obj, 'hello'),
     Reflect.getOwnMetadata('name', obj),
     Reflect.getOwnMetadata('name', obj ,'hello')
    ])
    // 大家猜测一下 res 的值会是多少?