掌握 JS 高级编程基础 - Reflect Metadata(1)

所以但凡你在 JS 中遇到元数据编程,你就假想成自己在设计数据库就行了。

讲到这里,我就继续啰嗦两句,方便扩散思维。

HTML的  标签里可以定义  标签。 元素可提供有关页面的元信息(meta-information),比如针对搜索引擎和更新频度的描述和关键词。“keywords” 是一个经常被用到的,提高网站 SEO 绕不开的一个设置项(某些搜索引擎在遇到这些关键字时,会用这些关键字对文档进行分类):

在我们了解完 metadata 的概念后,我们可以开始学习如何使用它了。

如何开始metadata编程?

TypeScript 已经完整的实现了装饰器,后续的讲解默认都以 TS 环境(虽然 Babel 也可以,但是需要各种配置,比较繁琐)。

Reflect Metadata 是 15 年提出的一个提案,现在我们想要使用这个功能,可以借助仓库 reflect-metadata,先 npm 安装这个库:

npm i reflect-metadata --save

TypeScript 支持为带有 装饰器 的声明 生成元数据。你需要在命令行或 tsconfig.json里启用emitDecoratorMetadata编译器选项:

{

“compilerOptions”: {

“target”: “ES5”,

“experimentalDecorators”: true,

“emitDecoratorMetadata”: true

}

}

当启用后,只要reflect-metadata库被引入了,设计阶段添加的类型信息可以在运行时使用。

基础用法

严格地说,元数据(metadata)和 装饰器(Decorator) 是 EcmaScript 中两个独立的部分。 然而,如果你想实现像是 反射这样的能力,你总是同时需要它们。

比如下方这个例子:

// 要引入这个库

import “reflect-metadata”;

@Reflect.metadata(‘name’, ‘Person’)

class Person {

@Reflect.metadata(‘words’, ‘hello world’)

public speak(): string {

return ‘hello world’

}

}

Reflect.getMetadata(‘name’, Person) // ‘Person’

Reflect.getMetadata(‘words’, new Person(), ‘speak’) // ‘hello world’

// 这里为什么要用 new A(),用 A 不行么?后文会讲到

是不是很简单?对照这个例子,我们再引出 Metadata 的四个概念:

  1. Metadata Key {Any}(后文简写 k)。元数据的 Key,对于一个对象来说,它可以有很多元数据,每一个元数据都对应有一个 Key。一个很简单的例子就是说,你可以在一个对象上面设置一个叫做 ‘name’ 的 Key 用来设置他的名字,用一个 ‘created time’ 的 Key 来表示他创建的时间。这个 Key 可以是任意类型。在后面会讲到内部本质就是一个 Map 对象。

  2. Metadata Value {Any} (后文简写 v)。元数据的类型,任意类型都行。

  3. Target {Object} (后文简写 o)。表示要在这个对象上面添加元数据

  4. Property {String|Symbol} (后文简写 p)。用于设置在哪个属性上添加元数据。大家可能会想,这个是干什么用的,不是可以在对象上面添加元数据了么?其实不仅仅可以在对象上面添加元数据,甚至还可以在对象的属性上面添加元数据。其实大家可以这样理解,当你给一个对象定义元数据的时候,相当于你是默认指定了 undefined 作为 Property。

了解了这 4 个概念,我们阅读对应的 API 就会比较容易。

API设计

最为全面的 API 可以查阅标准文档: https://rbuckton.github.io/reflect-metadata/

API 的设计也是非常清晰明了,metadata 毕竟也属于 “数据”,那么对应的 API 就是跟数据库的 CURD 增删改查的操作相对应的。

先罗列一下完整的 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

}

  创建元数据**(Reflect.metadata/Reflect.defineMetadata)**

两种方式创建,其最本质都是调用源码中 OrdinaryDefineOwnMetadata 方法,所以结果是一样的,只是使用方式不一样。

一种是通过装饰器声明方式创建,推荐的方式,也是很主流的一种方式:

// 装饰类

@Reflect.metadata(key, value)

class Example {

}

// 装饰静态属性

class Example {

@Reflect.metadata(key, value)

static staticProperty;

}

// 装饰实例属性

class Example {

@Reflect.metadata(key, value)

property;

}

// 装饰静态方法

class Example {

@Reflect.metadata(key, value)

static staticMethod() { }

}

// 装饰实例方法

class Example {

@Reflect.metadata(key, value)

method() { }

}

另一种就是 “事后”(类创建完后)再给目标对象创建元数据:

class Example {

// property declarations are not part of ES6, though they are valid in TypeScript:

// static staticProperty;

// property;

constructor§ { }

static staticMethod§ { }

method§ { }

}

// 给 类本身 加元数据

Reflect.defineMetadata(“custom:annotation”, options, Example);

// 给 类的静态属性 新增元数据

Reflect.defineMetadata(“custom:annotation”, options, Example, “staticProperty”);

// 给 类的实例属性 新增元数据,注意是加在 prototype 上

Reflect.defineMetadata(“custom:annotation”, options, Example.prototype, “property”);

// 给 类的静态方法 新增元数据

Reflect.defineMetadata(“custom:annotation”, options, Example, “staticMethod”);

// 给 类的实例方法 新增元数据,注意是加在 prototype 上

Reflect.defineMetadata(“custom:annotation”, options, Example.prototype, “method”);

这两种方式之间的联系就是,装饰器方法 Reflect.metadata 其实内部就是用 Reflect.defineMetadata 实现的:

function metadata(metadataKey: any, metadataValue: any): Decorator {

return (target, key?) => Reflect.defineMetadata(“custom:annotation”, options, target, key);

}

  查询****元数据

这里的 “查询” 是加引号的,因为元数据的查询的功能很简单,只要该元数据即可,不需获取要写 SQL 那么复杂。由于涉及到原型链,所以每个 API 都存在一对,用以区分是否包含原型链的操作。

  • 获取元数据(getMetadata/getOwnMetadata)

这里涉及到两个相关的 API,getMetadata/getOwnMetadata 两个方法,它们之间的区别前者会包含原型链查找,后者不会查找原型链。

(类似于 Object.hasProperty 和 Object.hasOwnProperty 这两个方法的区别)

举例如下:

class A {

@Reflect.metadata(‘name’, ‘hello’)

hello() {}

}

const t1 = new A()

const t2 = new A()

// 给 t2.hello 新增元数据内容 otherName -> world

// 此时会新增到 t2.prototype(原型链) 上(注意不是 A.prototype 上)

Reflect.defineMetadata(‘otherName’, ‘world’, t2, ‘hello’);

// 首先,t1/t2 自己能获取原本的原型链 name 对应的元数据

Reflect.getMetadata(‘name’, t1, ‘hello’) // ‘hello’

Reflect.getMetadata(‘name’, t2, ‘hello’) // ‘hello’

// t1 自身上没定义 otherName 的元数据

Reflect.getMetadata(‘otherName’, t1, ‘hello’) // undefined

// t2 自身定义了 otherName 的元数据

Reflect.getMetadata(‘otherName’, t2, ‘hello’) // ‘world’

// t2 自身上只有 otherName 这个元数据,没有 name 元数据(该元数据来自父级)

Reflect.getOwnMetadata(‘name’, t2, ‘hello’) // undefined

Reflect.getOwnMetadata(‘otherName’, t2, ‘hello’) // ‘world’

  • 判断是否存在元数据(hasMetadata/hasOwnMetadata)

同样的涉及到两个相关的 API,hasMetadata/hasOwnMetadata 两个方法,它们两者调用方式一样,唯一的区别是前者会包含原型链查找,后者不会查找原型链。而且入参含义跟getMetadata/getOwnMetadata 两个方法是一样的:

class Example {

// property declarations are not part of ES6, though they are valid in TypeScript:

// static staticProperty;

// property;

constructor§ { }

static staticMethod§ { }

method§ { }

}

// 类本身 上是否存在元数据

result = Reflect.hasMetadata(“custom:annotation”, options, Example);

// 类的静态属性 上是否存在元数据

result = Reflect.hasMetadata(“custom:annotation”, options, Example, “staticProperty”);

// 类的实例属性 上是否存在元数据,注意是在 prototype 上

result = Reflect.hasMetadata(“custom:annotation”, options, Example.prototype, “property”);

// 类的静态方法 上是否存在元数据

result = Reflect.hasMetadata(“custom:annotation”, options, Example, “staticMethod”);

// 类的实例方法 上是否存在元数据,注意是在 prototype 上

result = Reflect.hasMetadata(“custom:annotation”, options, Example.prototype, “method”);

  • 获取 metaKeys(getMetadataKeys/getOwnMetadataKeys)

同样的涉及到两个相关的 API,getMetadataKeys/getOwnMetadataKeys 两个方法。

class Example {

// property declarations are not part of ES6, though they are valid in TypeScript:

// static staticProperty;

// property;

constructor§ { }

static staticMethod§ { }

method§ { }

}

// 获取 类本身 的元数据 keys

result = Reflect.getMetadataKeys(Example);

// 获取 类的静态属性 的元数据 keys

result = Reflect.getMetadataKeys(Example, “staticProperty”);

// 获取 类的实例属性 的元数据 keys,注意是从 prototype 上获取

result = Reflect.getMetadataKeys(Example.prototype, “property”);

// 获取 类的静态方法 的元数据 keys

result = Reflect.getMetadataKeys(Example, “staticMethod”);

// 获取 类的实例方法 的元数据 keys,注意是从 prototype 上获取

result = Reflect.getMetadataKeys(Example.prototype, “method”);

  删除****元数据

用于删除元数据的 API 是 deleteMetadata,如果对象上有该元数据, 则会删除成功返回 true, 否则返回 false。因为比较简单, 就直接上例子了:

const type = ‘type’

@Reflect.metadata(type, ‘class’)

class DeleteMetadata {

@Reflect.metadata(type, ‘static’)

static staticMethod() {}

}

const res1 = Reflect.deleteMetadata(type, DeleteMetadata)

const res2 = Reflect.deleteMetadata(type, DeleteMetadata, ‘staticMethod’)

const res3 = Reflect.deleteMetadata(type, DeleteMetadata)

console.log(res1, res2, res3) // true true false

(注意并没有 deleteOwnMetadata 这样的 API)

TS福利:design:类型元数据

这里需要提及一点的是,在 TS 中的 reflect-metadata 是经过扩展[5]的(也就是说功能更加强劲),我们看个例子:

import “reflect-metadata”;

class Foo {

@Reflect.metadata(“hello”, “world”)

public say(a: string): string {

return ‘foo’;

}

}

我们查看其转换出来的 JS 代码,你会发现处理我们自己的元数据 “hello”->“world” 之外,还会额外给我们添加 “design:type”、“design:paramtypes” 和 “design:returntype” 这 3 个类型相关的元数据。

之所以会有,这是因为我们在 TS 中开启了 emitDecoratorMetadata(https://www.typescriptlang.org/tsconfig#emitDecoratorMetadata)编译选项,这样 TS 在编译的时候会将类型元数据自动添加上去。这也是 TS 强类型编程带来的额外好处 —— 换言之如果你用 ES6 编程,需要自己加这 3 个元数据。

(当然,如果你关闭 emitDecoratorMetadata选项,也就没有这 3 个类型元数据了)

design:xxx 的内容分别表示什么意思呢[4]?

  • design:type 表示被装饰的对象是什么类型, 比如是字符串? 数字? 还是函数等

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

这个月马上就又要过去了,还在找工作的小伙伴要做好准备了,小编整理了大厂java程序员面试涉及到的绝大部分面试题及答案,希望能帮助到大家

在这里插入图片描述

在这里插入图片描述

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**[外链图片转存中…(img-FvxJFo6t-1713703795610)]

[外链图片转存中…(img-YMIPs72I-1713703795611)]

[外链图片转存中…(img-ow4PhbGt-1713703795611)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

这个月马上就又要过去了,还在找工作的小伙伴要做好准备了,小编整理了大厂java程序员面试涉及到的绝大部分面试题及答案,希望能帮助到大家

[外链图片转存中…(img-nB7XSHZs-1713703795611)]

[外链图片转存中…(img-jEwA7eKr-1713703795612)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScript 的 Proxy 和 Reflect 是 ECMAScript 6 引入的新特性。它们可以一起使用来创建代理对象,以拦截对对象的访问和操作,从而提供更灵活的控制和定制行为。 Proxy 是一个 JavaScript 内置对象,它允许你拦截并定义基本操作的自定义行为,例如属性访问、函数调用、对象构造等。Reflect 是一个 JavaScript 内置对象,它提供了一组方法,用于在 Proxy 对象上执行默认行为。 使用 Proxy 和 Reflect,开发人员可以创建具有自定义行为的对象。例如,您可以使用 Proxy 拦截对对象属性的访问,并在 Reflect 上调用默认行为来获取或设置该属性的值。这种灵活性使得开发人员能够更好地控制和自定义应用程序的行为。 下面是一个简单的例子,展示了如何使用 Proxy 和 Reflect 创建一个代理对象: ```javascript const obj = { name: 'John', age: 30 }; const handler = { get(target, prop, receiver) { console.log(`Getting ${prop} property`); return Reflect.get(target, prop, receiver); }, set(target, prop, value, receiver) { console.log(`Setting ${prop} property to ${value}`); return Reflect.set(target, prop, value, receiver); } }; const proxyObj = new Proxy(obj, handler); console.log(proxyObj.name); proxyObj.age = 35; ``` 在上面的例子中,我们创建了一个名为 obj 的对象,并将其传递给一个名为 handler 的代理处理程序。处理程序定义了对 get 和 set 操作的拦截,其中在每个操作中都使用 Reflect 调用默认行为。然后,我们创建了一个名为 proxyObj 的代理对象,并将其设置为 obj 的代理。最后,我们访问了 proxyObj 的属性,并将其设置为一个新值。 当我们运行上面的代码时,我们会看到以下输出: ``` Getting name property John Setting age property to 35 ``` 从输出中可以看出,代理处理程序成功地拦截了对代理对象的访问和操作,并使用 Reflect 调用了默认行为。这样,我们就可以更好地控制和自定义我们的应用程序行为。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值