JavaScript 依賴注入實現

隨著AngularJS的流行,依賴注入開始在JavaScript領域獲得不少的關注。 DI最突出的好處在于開發可復用可測試的代碼單元。 本文以簡易的代碼解釋DI的實現機制,更多對DI優缺點的討論可參考: 什么時候應該使用依賴注入 一文。

一個基本的DI用例

每個模塊聲明自己的依賴,并提供自己的服務。例如:

di.service('foo', ['bar'], function foo(bar){
    function Foo(){
        this.bar = bar;
    }
    this.prototype.greeting = function(){
        console.log('hello, world');
    }
    return Foo;
});
var foo = di.container.get('foo');
foo.greeting();

注意依賴注入和CommonJS(或AMD)的區別, foo 只需要聲明其依賴項 bar 而不需要主動獲取。 正是這一點使得 function foo 對依賴所處的位置和構建方法都完全無知, function foo 成為可測試、可復用的代碼單元。

DI框架的設計

注冊服務和使用服務應該在不同時期進行。 作為一種特殊的依賴解決工具,DI框架將軟件單元的生命周期分為注冊階段和運行階段。 上述例子中,在注冊階段提供 foo 和 bar 服務,在運行階段獲取并使用這些服務。 多數DI框架都采取lazy construction的策略,該策略也避免了在注冊階段進行構造的困難。

服務的定制可以在注冊階段后運行階段前進行。 AngularJS 1 引入配置階段來定制這些服務,其Provider可以理解為一個特化的工廠對象。 BottleJS 則使用修飾器和中間件來支持對服務的定制。

使用IoC容器來索引服務實例或存儲服務提供者。 當有人提供服務時就把它加入到容器中, 當有人使用服務時就從容器中查找提供者并生成一個服務實例。 通常服務的實例可以被緩存。

DI框架的實現

先來實現最常見的接口函數 .service() ,該接口用來注冊一個服務的構造器。 被傳入的函數將會被進行 new 操作。

var di = {
    container: {}
};

di.service = function(name, Constructor) {
    defineLazyProperty(name, () => new Constructor());
};

function defineLazyProperty(name, getter){
    Object.defineProperty(di.container, name, {
        configurable: true,
        get: function() {
            var obj = getter(container);
            Object.defineProperty(di.container, name, {
                configurable: false
                value: obj
            });
            return obj;
        }
    });
}

Object.defineProperty 在這里用來做服務緩存。 只在第一次構建服務時調用構造器,后續的訪問就是直接讀取IoC容器的屬性。 它是ES5的標準方法 兼容性非常好 。 有了 defineLazyProperty() 方法,這些常用的注冊接口實現就很直觀了:

di.factory = function(name, factory) {
    return defineLazyProperty(name, factory);
};

di.provider = function(name, Provider) {
    return defineLazyProperty(name, function(){
        var provider = new Provider();
        return provider.$get();
    });
};

di.value = function(name, val) {
    return defineLazyProperty(name, () => val);
};

服務的定制接口就不再贅述了,值得一提的是統一的服務定制需要統一的服務構造方法, 而不是直接調用 .defineLazyProperty() 生成屬性。 AngularJS 中這些策略都由Provider來實現, 其他的所有服務注冊方法都借由Provider來實現。

 

來自:http://harttle.com/2016/11/19/javascript-dependency-injection-implementation.html

 

 本文由用戶 晚飯吃不吃 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!