解讀 IoC 框架 InversifyJS

原文連接git

InversityJS 是一個 IoC 框架。IoC(Inversion of Control) 包括依賴注入(Dependency Injection) 和依賴查詢(Dependency Lookup)。github

相比於類繼承的方式,控制反轉解耦了父類和子類的聯繫。api

案例解析

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 容器;this

2.經過 container.get() 獲取 container.bind().to(target) 中的目標對象(這裏指 Music);code

3.若是目標對象中的 constructor() 裏有 @inject(), 則將相應的實例(這裏指 PopMusic 與 classicalMusic 的實例)看成構造函數的參數'注入';對象

inject/injectable 相關源碼

inject 源碼簡化以下:blog

// 這是一個屬性裝飾器
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
    }
  }
}

相關連接

相關文章
相關標籤/搜索