如何在JavaScript中使用ReflectMetadata-創(chuàng)新互聯(lián)

今天就跟大家聊聊有關(guān)如何在JavaScript中使用Reflect Metadata,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

創(chuàng)新互聯(lián)長期為超過千家客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為大通企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計、成都網(wǎng)站制作,大通網(wǎng)站改版等技術(shù)服務(wù)。擁有10年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。

Metadata

想必對于其他語言的 Coder 來說,比如說 Java 或者 C#,Metadata 是很熟悉的。最簡單的莫過于通過反射來獲取類屬性上面的批注(在 JS 當中,也就是所謂的裝飾器)。從而可以更加優(yōu)雅的對代碼進行控制。

而 JS 現(xiàn)在有裝飾器,雖然現(xiàn)在還在 Stage2 階段。但是 JS 的裝飾器更多的是存在于對函數(shù)或者屬性進行一些操作,比如修改他們的值,代理變量,自動綁定 this 等等功能。

所以,后文當中我就使用 TypeScript 來進行講解,因為 TypeScript 已經(jīng)完整的實現(xiàn)了裝飾器。

雖然 Babel 也可以,但是需要各種配置,人懶,不想配置那么多。

但是卻無法實現(xiàn)通過反射來獲取究竟有哪些裝飾器添加到這個類/方法上。

于是 Reflect Metadata 應(yīng)運而生。

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 不行么?后文會講到

是不是很簡單,那么我簡單來介紹一下~

概念

首先,在這里有四個概念要區(qū)分一下:

  1. Metadata Key {Any} 后文簡寫 k。元數(shù)據(jù)的 Key,對于一個對象來說,他可以有很多元數(shù)據(jù),每一個元數(shù)據(jù)都對應(yīng)有一個 Key。一個很簡單的例子就是說,你可以在一個對象上面設(shè)置一個叫做 'name' 的 Key 用來設(shè)置他的名字,用一個 'created time' 的 Key 來表示他創(chuàng)建的時間。這個 Key 可以是任意類型。在后面會講到內(nèi)部本質(zhì)就是一個 Map 對象。

  2. Metadata Value {Any} 后文簡寫 v。元數(shù)據(jù)的類型,任意類型都行。

  3. Target {Object} 后文簡寫 o。表示要在這個對象上面添加元數(shù)據(jù)

  4. Property {String|Symbol} 后文簡寫 p。用于設(shè)置在那個屬性上面添加元數(shù)據(jù)。大家可能會想,這個是干什么用的,不是可以在對象上面添加元數(shù)據(jù)了么?其實不僅僅可以在對象上面添加元數(shù)據(jù),甚至還可以在對象的屬性上面添加元數(shù)據(jù)。其實大家可以這樣理解,當你給一個對象定義元數(shù)據(jù)的時候,相當于你是默認指定了 undefined 作為 Property。 下面有一個例子大家可以看一下。

大家明白了上面的概念之后,我之前給的那個例子就很簡單了~不用我多說了。

安裝/使用

下面不如正題,我們怎么開始使用 Reflect Metadata 呢?

首先,你需要安裝 reflect-metadata polyfill,然后引入之后就可以看到在 Reflect 對象下面有很多關(guān)于 Metadata 的函數(shù)了。因為這個還沒有進入正式的協(xié)議,所以需要安裝墊片使用。

啥,Reflect 是啥,一個全局變量而已。

你不需要擔心這個墊片的質(zhì)量,因為連 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 的值會是多少?

想好了么?再給你 10 秒鐘

10
9
8
7
6
5
4
3
2
1

res

[
 ['A', undefined, 'A', undefined],
 [undefined, 'hello', undefined, undefined],
 [undefined, 'hello', undefined, 'hello'],
]

那么我來解釋一下為什么回是這樣的結(jié)果。

首先所有的對類的修飾,都是定義在類這個對象上面的,而所有的對類的屬性或者方法的修飾,都是定義在類的原型上面的,并且以屬性或者方法的 key 作為 property,這也就是為什么 getMetadata 會產(chǎn)生這樣的效果了。

那么帶 Own 的又是什么情況呢?

這就要從元數(shù)據(jù)的查找規(guī)則開始講起了

原型鏈查找

類似于類的繼承,查找元數(shù)據(jù)的方式也是通過原型鏈進行的。

就像是上面那個例子,我實例化了一個 new A(),但是我依舊可以找到他原型鏈上的元數(shù)據(jù)。

舉個例子

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'

用途

其實所有的用途都是一個目的,給對象添加額外的信息,但是不影響對象的結(jié)構(gòu)。這一點很重要,當你給對象添加了一個原信息的時候,對象是不會有任何的變化的,不會多 property,也不會有的 property 被修改了。
但是可以衍生出很多其他的用途。

  • Anuglar 中對特殊字段進行修飾 (Input),從而提升代碼的可讀性。

  • 可以讓裝飾器擁有真正裝飾對象而不改變對象的能力。讓對象擁有更多語義上的功能。

API

namespace Reflect {
 // 用于裝飾器
 metadata(k, v): (target, property?) => void
 
 // 在對象上面定義元數(shù)據(jù)
 defineMetadata(k, v, o, p?): void
 
 // 是否存在元數(shù)據(jù)
 hasMetadata(k, o, p?): boolean
 hasOwnMetadata(k, o, p?): boolean
 
 // 獲取元數(shù)據(jù)
 getMetadata(k, o, p?): any
 getOwnMetadata(k, o, p?): any
 
 // 獲取所有元數(shù)據(jù)的 Key
 getMetadataKeys(o, p?): any[]
 getOwnMetadataKeys(o, p?): any[]
 
 // 刪除元數(shù)據(jù)
 deleteMetadata(k, o, p?): boolean
}

大家可能注意到,針對某些操作,會有 Own 的函數(shù)。這是因為有的操作是可以通過原型鏈進行操作的。這個后文講解。

深入 Reflect Metadata

實現(xiàn)原理

如果你去翻看官網(wǎng)的文檔,他會和你說,所有的元數(shù)據(jù)都是存在于對象下面的 [[Metadata]] 屬性下面。一開始我也是這樣認為的,新建一個 Symbol('Metadata'),然后將元數(shù)據(jù)放到這個 Symbol 對應(yīng)的 Property 當中。直到我看了源碼才發(fā)現(xiàn)并不是這樣。請看例子

@Reflect.metadata('name', 'A')
class A {}

Object.getOwnPropertySymbols(A) // []

哈哈,并沒有所謂的 Symbol,那么這些元數(shù)據(jù)都存在在哪里呢?

其實是內(nèi)部的一個 WeakMap 中。他正是利用了 WeakMap 不增加引用計數(shù)的特點,將對象作為 Key,元數(shù)據(jù)集合作為 Value,存到 WeakMap 中去。

如果你認真探尋的話,你會發(fā)現(xiàn)其內(nèi)部的數(shù)據(jù)結(jié)構(gòu)其實是這樣的

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

是不是超級繞,但是我們從調(diào)用的角度來思考,這就一點都不繞了。

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

看完上述內(nèi)容,你們對如何在JavaScript中使用Reflect Metadata有進一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注創(chuàng)新互聯(lián)成都網(wǎng)站設(shè)計公司行業(yè)資訊頻道,感謝大家的支持。

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。

文章名稱:如何在JavaScript中使用ReflectMetadata-創(chuàng)新互聯(lián)
標題鏈接:http://muchs.cn/article16/cedgdg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、微信公眾號、企業(yè)建站、服務(wù)器托管、Google、App開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

微信小程序開發(fā)