解讀 IoC 框架 InversifyJS
ofollow,noindex" target="_blank">原文連結
InversityJS 是一個 IoC 框架。IoC(Inversion of Control) 包括依賴注入(Dependency Injection) 和依賴查詢(Dependency Lookup)。
相比於類繼承的方式,控制反轉解耦了父類和子類的聯絡。
案例解析
import 'reflect-metadata' import { inject, injectable, Container } from 'inversify' const container = new Container() @injectable() class PopMusic { getName() { return '流行音樂' } } container.bind('request1').to(PopMusic) @injectable() class ClassicalMusic { getName() { return '古典音樂' } } container.bind('request2').to(ClassicalMusic) @injectable() class Music { pm: any cm: any constructor( @inject('request1') popMusic: any, @inject('request2') classicalMusic: any) { this.pm = popMusic this.cm = classicalMusic } getName() { const result = this.pm.getName() + this.cm.getName() return result } } container.bind('Plan').to(Music) const music: any = container.get('Plan') console.log(music.getName()) // 流行音樂古典音樂
上述案例可以抽象為下圖:
虛線表示可以注入,但在程式碼中沒有表現出來。
程式碼流程可概括如下:
1.將所有相關類(這裡指 Music、popMusic、classicMusic) 通過 @injectable
宣告進 container
容器;
2.通過 container.get()
獲取 container.bind().to(target)
中的目標物件(這裡指 Music);
3.如果目標物件中的 constructor() 裡有 @inject()
, 則將相應的例項(這裡指 PopMusic 與 classicalMusic 的例項)當作建構函式的引數'注入';
inject/injectable 相關原始碼
inject 原始碼簡化如下:
// 這是一個屬性裝飾器 function inject(serviceIdentifier) { return function (target, targetKey) { const metadataValue = { [targetKey]: [Metadata { key: 'inject', value: serviceIdentifier })] } Reflect.defineMetadata('inversify:tagged_props', metadataValue, target.constructor); } }
injectable 原始碼簡化如下:
// 這是一個類裝飾器 function injectable() { return function (target) { const metadataValue = [] Reflect.defineMetadata('inversify:paramtypes', metadataValue, target) return target } }
從簡化版原始碼中可以看到 inject/injectable 最終是對 Reflect.defineMetadata()
的一個使用。可以將 metadata 看成是一種相對高效的資料結構。
reflect-metadata
InversityJS 深度結合了 reflect-metadata , reflect-metadata 在 Reflect 基礎上對其 api 進行了擴充套件。
metadata 本質上是一個 WeakMap
物件。擴充套件: Map 和 WeakMap 的區別
Reflect.defineMetadata(metadataKey, metadataValue, target[, propertyKey])
簡化版實現如下:
const Metadata = new WeakMap() function defineMetadata(metadataKey, metadataValue, target, propertyKey) { metadataMap = new Map() metadataMap.set(metadataKey, metadataValue) targetMetadata = new Map() targetMetadata.set(propertyKey, metadataMap) Metadata.set(target, targetMetadata) }
Reflect.getOwnMetadata(metadataKey, target[, propertyKey])
簡化版實現如下:
function getOwnMetadata(metadataKey, target, propertyKey) { var targetMetadata = Metadata.get(target) var metadataMap = targetMetadata.get(propertyKey) return metadataMap.get(metadataKey) }
其資料結構可表示如下:
WeakMap { target: Map { propertyKey: Map { metadataKey: metadataValue } } }