最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Reflect Metadata(元数据)学习笔记

    正文概述 掘金(VELOMA)   2021-06-16   812

    decorate方法

    declare type ClassDecorator = <TFunction exntends Function>(target: TFunction) ⇒ TFunction | void;

    function decorate(decorators: ClassDecorator[], target: Function): Function;

    • 对类的装饰

      对类的装饰该方法有几个参数, 分别是:

      • @param {Array} decorators - 装饰器的数组
      • @param {Object} target - 目标对象
      • @returns 返回应用提供的装饰器后的值
      • 注意: 装饰器应用是与array的位置方向相反, 为从右往左.

      来个?:给TestCkassDecorator类添加或者修改sayName方法

      const classDecorator: ClassDecorator = target => {
        target.prototype.sayName = () => console.log('veloma');
      	// return target; 这里可以return也可以不return, 因为target是一个对象引用
      }
      
      class TestClassDecorator {
      constructor(public name = '') {}
      sayName() {
              console.log(this.name);
      }
      }
      
      Reflect.decorate([classDecorator], TestClassDecorator); // 对类进行修饰
      const t = new TestClassDecorator('nihao');
      
      t.sayName(); // veloma
      

      注意:在classDecorator中传入的target, 只能修改其prototype的方法, 不能修改其属性, 因为其属性是ready-only

    • 对属性或方法的装饰

      对属性或方法的修饰有几个参数, 分别是:

      • @param {Array} decorators - 装饰器的集合
      • @param {Object} target - 目标对象
      • @param {string} key - 要装饰的属性名称
      • @param {Object} descriptor - 该属性的描述

      注意: descriptor分为两种, 一种是数据描述符, 一种是存取描述符

      // 数据描述符
      {
          value: 'aaa',
          configurable: true,
          writable: true,
          enumerable: true
      }
      // 存取描述符
      {
          get() {return 1},
          set() { console.log('set') },
          configurable: true,
          enumerable: true
      }
      

      属性装饰器: AOP编程, 在原方法的后面加上操作

      const propertyDecorator: PropertyDecorator = (target, propertyKey) => {
      const origin = target[propertyKey];
          target[propertyKey] = () => {
            origin.call(target);
            console.log('added override');
          }
      }
      
      class PropertyAndMethodExample {
          static staticProperty() {
              console.log('im static property');
          }
      
          method() {
              console.log('im one instance method');
          }
      }
      
      // 装饰PropertyAndMethodExample的staticProperty方法(静态方法)
      Reflect.decorate([propertyDecorator], PropertyAndMethodExample, 'staticProperty');
      PropertyAndMethodExample.staticProperty(); // im static property \n added override
      

      方法装饰器

      const methodDecorator: MethodDecorator = (target, propertyKey, descriptor) => {
      	// 将其描述改为不可编辑
      	descriptor.configurable = false;
      	descriptor.writable = false;
      	return descriptor;
      }
      
      class PropertyAndMethodExample {
          static staticProperty() {
              console.log('im static property');
          }
      
      	method() {
              console.log('im one instance method');
      	}
      }
      
      // 获取原descriptor
      let descriptor = Object.getOwnPropertyDescriptor(PropertyAndMethodExample.prototype, 'method');
      
      // 获取修改后的descriptor
      descriptor = Reflect.decorate([methodDecorator], PropertyAndMethodExample, 'method', descriptor);
      // 将修改后的descriptor添加到对应的方法上
      Reflect.defineProperty(PropertyAndMethodExample.prototype, 'method', descriptor);
      
      const example = new PropertyAndMethodExample();
      example.method = () => console.log('override'); // 报错: 因为已经将该方法(属性)的writable描述符设置为false
      

    metadata方法

    /**
    * @param {string} metadataKey - 元数据入口的key
    * @param {*} metadataValue 元数据入口的value
    * @returns 装饰器函数
    */
    function metadata(metadataKey: any, metadataValue: any) {
        (target: Function): void;
        (target: Object, propertyKey: string | symbol): void;
    }
    

    实例

    const nameSymbol = Symbol('veloma');
    
    // 类元数据
    @Reflect.metadata('class', 'class');
    class MetaDataClass {
        // 实例属性元数据
        @Reflect.metadata(nameSymbol, 'nihao')
        public name = 'origin';
    
        // 实例方法元数据
        @Reflect.metadata('getName', 'getName')
        public getName() {}
    
        // 静态方法元数据
        @Reflect.metadata('static', 'static')
        static staticMethod() {}
    }
    
    // 创建元数据类的实例
    const metadataInstance = new MetaDataClass();
    
    // 获取MetaDataClass的name元数据
    const value = Reflect.getMetadata('name', MetaDataClass); // undefined
    // 获取实例中name属性的nameSymbol元数据
    const name = Reflect.getMetadata(nameSymbol, metadataInstance, 'name'); // nihao
    // 获取实例中getName属性的getName元数据
    const methodVal = Reflect.getMetadata('getName', metadataInstance, 'getName'); // getName
    // 获取元数据类的staticMethod属性的static元数据
    const staticVal = Reflect.getMetadata('static', MetaDataClass, 'staticMethod'); // static
    
    console.log(value, name, methodVal, staticVal); // undefined nihao getName static
    

    defineMetadata方法

    该方法是metadata的定义版本, 也就是非@版本, 会多穿一个参数target, 表示待装饰的对象

    /**
    * @param {string} metadataKey - 设置或获取时的key
    * @param {*} metadataValue - 元数据内容
    * @param {Object} target - 待装饰的target
    * @param {string} targetKey - target的property
    */
    function defineMetadata(metadataKey: any, metadataValue: any, target: Object, targetKey: string | symbol): void;
    

    示例

    class DefineMetadata {
        static staticMethod() {}
        static staticProperty = 'static';
        getName() {}
    }
    
    const type = 'type';
    // 给DefineMetadata设置元数据type, 值为class
    Reflect.defineMetadata(type, 'class', DefineMetadata);
    // 给DefineMetadata.staticMethod设置元数据type, 值为staticMethod
    Reflect.defineMetadata(type, 'staticMethod', DefineMetadata.staticMethod);
    // 给DefineMeatadata.prorotype.getName设置元数据type, 值为method
    Reflect.defineMetadata(type, 'method', DefineMetadata.prorotype.getName);
    // 给DefineMetadata的staticProperty属性设置元数据type, 值为staticProperty
    Reflect.defineMetadata(type, 'staticProperty', DefineMetadata, 'staticProperty');
    
    // 获取DefineMetadata身上的type元数据
    const t1 = Reflect.getMetadata(type, DefineMetadata); // class
    // 获取DefineMetadata.staticMethod身上的type元数据
    const t2 = Reflect.getMetadata(type, DefineMetadata.staticMethod); // staticMethod
    // 获取DefineMetadata.prototype.getName身上的type元数据
    const t3 = Reflect.getMetadata(type, DefineMetadata.prototype.getName); // method
    // 获取DefineMetadata上staticProperty属性的type元数据
    const t4 = Reflect.getMetadata(type, DefineMetadata, 'staticProperty'); // staticProperty
    
    console.log(t1, t2, t3, t4); // class staticMethod method staticProperty
    

    注意: t4定义和获取不一样的地方, 比如t2到t3都有两种写法, 一种就是将target转换为对应的对象且必须是对象, 以t2为例, 也可以写为

    Reflect.defineMetadata(type, 'staticMethods', DefineMetadata, 'staticMethod');
    const t2 = Reflect.getMetadata(type, DefineMetadata, 'staticMethod');
    

    注意: 这两种方式不能混合使用, 比如下面这种是不对的:

    Reflect.defineMetadata(type, 'staticMethod', DefineMetadata, 'staticMethod');
    const t2 = Reflect.getMetadata(type, DefineMetadata.staticMethod);
    

    hasMetadata方法

    该方法返回布尔值, 表明该target或其原型链上有没有对应的元数据

    /**
    * @param {string} metadataKey - 元数据的key
    * @param {Obejct} target - 定义的对象
    * @param {string} targetKey - 定义对象的属性(重载参数), 可选
    * @returns 在target或其原型链上返回true.
    */
    function hasMetadata(metadataKey: string, target: Object, targetKey?: symbol | string): boolean;
    

    示例

    const type = 'type';
    class HasMetadataClass {
        @Reflect.metadata(type, 'staticProperty')
        static staticProperty = '';
    }
    
    // 给HasMetadataClass定义一个type元数据, 值为class
    Reflect.defineMetadata(type, 'class', HasMetadataClass);
    const t1 = Reflect.hasMetadata(type, HasMetadataClass); // true
    const t2 = Reflect.hasMetadata(type, HasMetadataClass, 'staticProperty'); // true
    console.log(t1, t2); // true true
    

    其余的像实例属性/方法, 静态方法都以此类推

    hasOwnMetadata方法

    Object.prototype.hasOwnProperty类似, 是只查找对象上的元数据, 而不会继续想上查找原型链上的, 其余的跟hasMetadata一致

    const type = 'type';
    class Parent {
        @Reflect.metadata(type, 'getName')
        getName() {}
    }
    
    @Reflect.metadata(type, 'class')
    class HasOwnMetadataClass extends Parent {
        @Reflect.metadata(type, 'static')
        static staticProperty() {}
    
        @Reflect.metadata(type, 'method')
        method() {}
    }
    
    // 判断HasOwnMetadataClass有没有type这个元数据
    const t1 = Reflect.hasOwnProperty(type, HasOwnMetadataClass); // true
    // 判断HasOwnMetadataClass的staticProperty属性有没有type这个元数据
    const t2 = Reflect.hasOwnMetadata(type, HasOwnMetadataClass, 'staticProperty'); // true
    // 判断HasOwnMetadataClass.prototype的method属性有没有type这个元数据
    const t3 = Reflect.hasOwnMetadata(type, HasOwnMetadataClass.prototype, 'method'); // true
    // 判断HasOwnMetadataClass.prototype的getName属性有没有type这个元数据
    const t4 = Reflect.hasOwnMetadata(type, HasOwnMetadataClass.prototype, 'getName'); // false
    // 判断HasOwnMetadataClass.prototype的getName属性有没有type这个元数据, 这里的结果为true, 因为HasOwnMetadata.prototype上面没有这个属性, 但是HasOwnMetadata的原型链上有getName这个属性
    const t5 = Reflect.hasMetadata(type, HasOwnMetadataClass.prototype, 'getName'); // true
    
    console.log(t1, t2, t3, t4, t5); // true true true false true
    

    注意: t4和t5的区别

    getMetadata方法

    这个属性在之前验证各个属性的时候就已经使用过了, 就是用于获取target的元数据值, 会往原型链上找

    /**
    * @param {string} metadataKey - 元数据key
    * @param {Object} target - 元数据定义的target
    * @param {string} targetKey - 可选项, 是否选择target的某个key
    * @returns 如果找到了元数据则返回元数据值, 否则返回undefined
    **/
    function getMetadata(metadataKey: string, target: Object, targetKey?: string | symbol): any;
    

    getOwnMetadata方法

    与hasOwnMetadata和hasMetadata的区别一样, 是否往原型链上找

    getMetadataKeys方法

    类似Object.keys, 返回该target以及原型链上target的所有元数据的keys

    const type = 'type';
    @Reflect.metadata('parent', 'parent')
    class Parent {
        getName() {}
    }
    
    @Reflect.metadata(type, 'class')
    class HasOwnMetadataClass extends Parent {
        @Reflect.metadata(type, 'static')
        static staticProperty() {}
    
        @Reflect.metadata('bbb', 'method')
        @Reflect.metadata('aaa', 'method')
        method() {}
    }
    
    // 获取HasOwnMetadataClass身上以及原型链上的所有元数据
    const t1 = Reflect.getMetadataKeys(HasOwnMetadataClass); // type parent
    // 获取HasOwnMetadataClass中method属性身上的以及原型链上的所有元数据
    const t2 = Reflect.getMetadataKeys(HasOwnMetadataClass.prototype, 'method'); // aaa bbb
    
    

    t1很好理解, 因为会向上找原型链的parent, t2好像多了一些东西, desingn: 开头的, 先不管他, 看看aaa 和 bbb 的顺序是和我们添加的顺序是相反的, 还记得之前说过装饰器的顺序是从右到左的, 所以先应用bbb aaa在应用design:

    getOwnMetadataKeys方法

    跟getMetadataKeys一样, 只是不向原型链中查找

    deleteMetadata方法

    用于删除元数据

    /**
    * @param {string} metadataKey - 元数据key
    * @param {Object} target - 元数据定义的对象
    * @param {string} targetKey - 对象对应的key, 可选参数
    * @returns 如果对象上有该元数据, 返回true, 否则返回false
    */
    function deleteMetadata(metadataKey: string, target: Object, targetKey?: symbol | string): boolean;
    

    示例

    const type = 'type';
    @Reflect.metadata(type, 'class')
    class DeleteMetadata {
        @Reflect.metadata(type, 'static')
        static staticMethod() {}
    }
    
    // 删除DeleteMetadata身上的type元数据
    const res1 = Reflect.deleteMetadata(type, DeleteMetadata); // true
    // 删除DeleteMetadata上staticMethod属性身上的type元数据
    const res2 = Reflect.deleteMetadata(type, DelteMetadata, 'staticMethod'); // true
    // 再次删除DelelteMetadata身上的type元数据, 这次返回false, 因为在之前已经删除过了
    const res3 = Reflect.deleteMetadata(type, DelteMetadata); // false
    console.log(res1, res2, res3); // true true false
    

    design:

    好了还有一个问题没有解决, 就是之前说的在getMetadataKey时出现的design:xxx的内容是怎么来的, 表示什么意思呢?design:type 表示被装饰的对象是什么类型, 比如是字符串? 数字? 还是函数等. design:paramtypes 表示被装饰对象的参数类型, 是一个表示类型的数组, 如果不是函数, 则没有该key.design:returntype 表示被装饰对象的返回值属性, 比如字符串, 数字或函数等.

    示例

    @Reflect.metadata('type', 'class')
    class A {
        constructor(public name: string, public age: number) {}
    
        @Reflect.metadata(undefined, undefined)
        method(): boolean {
            return true;
        }
    }
    
    // 获取A的design:paramtypes元数据
    const t1 = Reflect.getMetadata('design:paramtypes', A); // [[Function: String], [Function: Number]]
    // 获取A.prototype上的method属性的design:returntype元数据
    const t2 = Reflect.getMetadata('design:returntype', A.prototype, 'method'); // [Function: Boolean]
    // 获取A.prototype上的method属性的design:type元数据
    const t3 = Reflect.getMetadata('design:type', A.prototype, 'method'); // [Function: Function]
    
    console.log(t1, t2, t3); // [[Function: String], [Function: Number], [Function: Boolean], [Function: Function]]
    

    注意:

    1. 没有装饰的target是get不到这些metadata的
    2. 必须手动指定类型, 无法进行推断, 比如method方法如果不指定, 返回值为boolean, 那么t2将是undefined
    3. 应用的顺序为: type → paramtypes → returntype

    起源地下载网 » Reflect Metadata(元数据)学习笔记

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元