亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

JavaScript中的依賴注入詳解

2019-11-20 12:54:32
字體:
來源:轉載
供稿:網友

計算機編程的世界其實就是一個將簡單的部分不斷抽象,并將這些抽象組織起來的過程。JavaScript也不例外,在我們使用JavaScript編寫應用時,我們是不是都會使用到別人編寫的代碼,例如一些著名的開源庫或者框架。隨著我們項目的增長,我們需要依賴的模塊變得越來越多,這個時候,如何有效的組織這些模塊就成了一個非常重要的問題。依賴注入解決的正是如何有效組織代碼依賴模塊的問題。你可能在一些框架或者庫種聽說過“依賴注入”這個詞,比如說著名的前端框架AngularJS,依賴注入就是其中一個非常重要的特性。但是,依賴注入根本就不是什么新鮮玩意,它在其他的編程語言例如PHP中已經存在已久。同時,依賴注入也沒有想象種那樣復雜。在本文中,我們將一起來學習JavaScript中的依賴注入的概念,深入淺出的講解如何編寫“依賴注入風格”的代碼。

目標設定

假設我們現在擁有兩個模塊。第一個模塊的作用是發送Ajax請求,而第二個模塊的作用則是用作路由。


var service = function() {
    return { name: 'Service' };
}
var router = function() {
    return { name: 'Router' };
}

這時,我們編寫了一個函數,它需要使用上面提到的兩個模塊:
復制代碼 代碼如下:

var doSomething = function(other) {
    var s = service();
    var r = router();
};

在這里,為了讓我們的代碼變得有趣一些,這個參數需要多接收幾個參數。當然,我們完全可以使用上面的代碼,但是無論從哪個方面來看上面的代碼都略顯得不那么靈活。要是我們需要使用的模塊名稱變為ServiceXML或者ServiceJSON該怎么辦?或者說如果我們基于測試的目的想要去使用一些假的模塊改怎么辦。這時,我們不能僅僅去編輯函數本身。因此我們需要做的第一件事情就是將依賴的模塊作為參數傳遞給函數,代碼如下所示:
復制代碼 代碼如下:

var doSomething = function(service, router, other) {
    var s = service();
    var r = router();
};

在上面的代碼中,我們完全傳遞了我們所需要的模塊。但是這又帶來了一個新的問題。假設我們在代碼的哥哥部分都調用了doSomething方法。這時,如果我們需要第三個依賴項該怎么辦。這個時候,去編輯所有的函數調用代碼并不是一個明智的方法。因此,我們需要一段代碼來幫助我們做這件事情。這就是依賴注入器試圖去解決的問題?,F在我們可以來定下我們的目標了:

1.我們應該能夠去注冊依賴項
2.依賴注入器應該接收一個函數,然后返回一個能夠獲取所需資源的函數
3.代碼不應該復雜,而應該簡單友好
4.依賴注入器應該保持傳遞的函數作用域
5.傳遞的函數應該能夠接收自定義的參數,而不僅僅是被描述的依賴項

requirejs/AMD方法

或許你已經聽說過了大名鼎鼎的requirejs,它是一個能夠很好的解決依賴注入問題的庫:

復制代碼 代碼如下:

define(['service', 'router'], function(service, router) {      
    // ...
});

requirejs的思想是首先我們應該去描述所需要的模塊,然后編寫你自己的函數。其中,參數的順序很重要。假設我們需要編寫一個叫做injector的模塊,它能夠實現類似的語法。
復制代碼 代碼如下:

var doSomething = injector.resolve(['service', 'router'], function(service, router, other) {
    expect(service().name).to.be('Service');
    expect(router().name).to.be('Router');
    expect(other).to.be('Other');
});
doSomething("Other");

在繼續往下之前,需要說明的一點是在doSomething的函數體中我們使用了expect.js這個斷言庫來確保代碼的正確性。這里有一點類似TDD(測試驅動開發)的思想。

現在我們正式開始編寫我們的injector模塊。首先它應該是一個單體,以便它能夠在我們應用的各個部分都擁有同樣的功能。

復制代碼 代碼如下:

var injector = {
    dependencies: {},
    register: function(key, value) {
        this.dependencies[key] = value;
    },
    resolve: function(deps, func, scope) {

    }
}


這個對象非常的簡單,其中只包含兩個函數以及一個用于存儲目的的變量。我們需要做的事情是檢查deps數組,然后在dependencies變量種尋找答案。剩余的部分,則是使用.apply方法去調用我們傳遞的func變量:
復制代碼 代碼如下:

resolve: function(deps, func, scope) {
    var args = [];
    for(var i=0; i<deps.length, d=deps[i]; i++) {
        if(this.dependencies[d]) {
            args.push(this.dependencies[d]);
        } else {
            throw new Error('Can/'t resolve ' + d);
        }
    }
    return function() {
        func.apply(scope || {}, args.concat(Array.prototype.slice.call(arguments, 0)));
    }       
}

如果你需要指定一個作用域,上面的代碼也能夠正常的運行。

在上面的代碼中,Array.prototype.slice.call(arguments, 0)的作用是將arguments變量轉換為一個真正的數組。到目前為止,我們的代碼可以完美的通過測試。但是這里的問題是我們必須要將需要的模塊寫兩次,而且不能夠隨意排列順序。額外的參數總是排在所有的依賴項之后。

反射(reflection)方法

根據維基百科中的解釋,反射(reflection)指的是程序可以在運行過程中,一個對象可以修改自己的結構和行為。在JavaScript中,簡單來說就是閱讀一個對象的源碼并且分析源碼的能力。還是回到我們的doSomething方法,如果你調用doSomething.toString()方法,你可以獲得下面的字符串:

復制代碼 代碼如下:

"function (service, router, other) {
    var s = service();
    var r = router();
}"

這樣一來,只要使用這個方法,我們就可以輕松的獲取到我們想要的參數,以及更重要的一點就是他們的名字。這也是AngularJS實現依賴注入所使用的方法。在AngularJS的代碼中,我們可以看到下面的正則表達式:
復制代碼 代碼如下:

/^function/s*[^/(]*/(/s*([^/)]*)/)/m

我們可以將resolve方法修改成如下所示的代碼:

復制代碼 代碼如下:

resolve: function() {
    var func, deps, scope, args = [], self = this;
    func = arguments[0];
    deps = func.toString().match(/^function/s*[^/(]*/(/s*([^/)]*)/)/m)[1].replace(/ /g, '').split(',');
    scope = arguments[1] || {};
    return function() {
        var a = Array.prototype.slice.call(arguments, 0);
        for(var i=0; i<deps.length; i++) {
            var d = deps[i];
            args.push(self.dependencies[d] && d != '' ? self.dependencies[d] : a.shift());
        }
        func.apply(scope || {}, args);
    }       
}

我們使用上面的正則表達式去匹配我們定義的函數,我們可以獲取到下面的結果:

復制代碼 代碼如下:

["function (service, router, other)", "service, router, other"]

此時,我們只需要第二項。但是一旦我們去除了多余的空格并以,來切分字符串以后,我們就得到了deps數組。下面的代碼就是我們進行修改的部分:
復制代碼 代碼如下:

var a = Array.prototype.slice.call(arguments, 0);
...
args.push(self.dependencies[d] && d != '' ? self.dependencies[d] : a.shift());

在上面的代碼中,我們遍歷了依賴項目,如果其中有缺失的項目,如果依賴項目中有缺失的部分,我們就從arguments對象中獲取。如果一個數組是空數組,那么使用shift方法將只會返回undefined,而不會拋出一個錯誤。到目前為止,新版本的injector看起來如下所示:

復制代碼 代碼如下:

var doSomething = injector.resolve(function(service, other, router) {
    expect(service().name).to.be('Service');
    expect(router().name).to.be('Router');
    expect(other).to.be('Other');
});
doSomething("Other");

在上面的代碼中,我們可以隨意混淆依賴項的順序。

但是,沒有什么是完美的。反射方法的依賴注入存在一個非常嚴重的問題。當代碼簡化時,會發生錯誤。這是因為在代碼簡化的過程中,參數的名稱發生了變化,這將導致依賴項無法解析。例如:

復制代碼 代碼如下:

var doSomething=function(e,t,n){var r=e();var i=t()}

因此我們需要下面的解決方案,就像AngularJS中那樣:
復制代碼 代碼如下:

var doSomething = injector.resolve(['service', 'router', function(service, router) {

}]);


這和最一開始看到的AMD的解決方案很類似,于是我們可以將上面兩種方法整合起來,最終代碼如下所示:
復制代碼 代碼如下:

var injector = {
    dependencies: {},
    register: function(key, value) {
        this.dependencies[key] = value;
    },
    resolve: function() {
        var func, deps, scope, args = [], self = this;
        if(typeof arguments[0] === 'string') {
            func = arguments[1];
            deps = arguments[0].replace(/ /g, '').split(',');
            scope = arguments[2] || {};
        } else {
            func = arguments[0];
            deps = func.toString().match(/^function/s*[^/(]*/(/s*([^/)]*)/)/m)[1].replace(/ /g, '').split(',');
            scope = arguments[1] || {};
        }
        return function() {
            var a = Array.prototype.slice.call(arguments, 0);
            for(var i=0; i<deps.length; i++) {
                var d = deps[i];
                args.push(self.dependencies[d] && d != '' ? self.dependencies[d] : a.shift());
            }
            func.apply(scope || {}, args);
        }       
    }
}

這一個版本的resolve方法可以接受兩個或者三個參數。下面是一段測試代碼:

復制代碼 代碼如下:

var doSomething = injector.resolve('router,,service', function(a, b, c) {
    expect(a().name).to.be('Router');
    expect(b).to.be('Other');
    expect(c().name).to.be('Service');
});
doSomething("Other");

你可能注意到了兩個逗號之間什么都沒有,這并不是錯誤。這個空缺是留給Other這個參數的。這就是我們控制參數順序的方法。

結語

在上面的內容中,我們介紹了幾種JavaScript中依賴注入的方法,希望本文能夠幫助你開始使用依賴注入這個技巧,并且寫出依賴注入風格的代碼。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品福利网站| 国产精品视频专区| 亚洲精品天天看| 亚洲区免费影片| 亚洲影视九九影院在线观看| 日韩av电影在线播放| 中文字幕视频在线免费欧美日韩综合在线看| 日韩中文字幕在线免费观看| 欧美国产在线电影| 国产一区二区精品丝袜| 久久九九热免费视频| 97久久超碰福利国产精品…| 久久久久久尹人网香蕉| 久久视频中文字幕| 欧美性猛交xxxx免费看漫画| 久久亚洲精品国产亚洲老地址| 国产精品第3页| 久久久久久成人精品| 久久人91精品久久久久久不卡| 国语自产精品视频在线看抢先版图片| 国产亚洲精品综合一区91| 日韩av影片在线观看| 久久久久久久国产精品视频| 国产福利精品视频| 国产亚洲一区二区精品| 精品国产一区二区三区久久狼黑人| 日韩av一卡二卡| 国产精品色午夜在线观看| 欧美国产日韩一区二区| 欧美性20hd另类| 亚洲免费一级电影| 久久99精品国产99久久6尤物| 国产日韩欧美在线看| 欧美怡春院一区二区三区| 欧美日韩国产精品一区二区三区四区| 欧美wwwwww| 欧美另类在线观看| 91伊人影院在线播放| 国产午夜精品一区理论片飘花| 国产精品视频免费在线观看| 最近2019中文字幕在线高清| 中文字幕精品影院| 91黄色8090| 欧美亚洲成人xxx| 成人午夜激情网| 亚洲精品永久免费精品| 亚洲综合小说区| 国产精品美女视频网站| 黑人巨大精品欧美一区二区一视频| 欧美激情日韩图片| 国产福利精品av综合导导航| 亚洲精品中文字幕有码专区| 91在线无精精品一区二区| 久久久久久久爱| 一本色道久久88综合日韩精品| 欧美在线亚洲在线| 欧美日韩精品在线播放| 亚洲人av在线影院| 欧美中文字幕视频| 97在线免费视频| 日韩一中文字幕| 亚洲成人在线视频播放| 亚洲经典中文字幕| 亚洲福利视频二区| 亚洲综合成人婷婷小说| 北条麻妃在线一区二区| 亚洲第一区中文字幕| 日韩毛片在线观看| www国产精品com| 尤物yw午夜国产精品视频明星| 久久夜色精品国产亚洲aⅴ| 国产在线视频一区| 欧美最猛性xxxxx亚洲精品| 97视频国产在线| 日本久久久久久久久| 在线观看国产精品日韩av| 亚洲天堂网站在线观看视频| 国产在线拍揄自揄视频不卡99| 清纯唯美日韩制服另类| 中文字幕一区二区精品| 91久久精品国产91久久| 国产精品海角社区在线观看| 欧美午夜精品在线| 国产精品自产拍在线观看中文| 色噜噜亚洲精品中文字幕| 国产精品观看在线亚洲人成网| 在线精品91av| 亚洲影视九九影院在线观看| 亚洲无线码在线一区观看| 狠狠躁夜夜躁人人躁婷婷91| 精品国产一区二区在线| 久久久久九九九九| 色婷婷综合成人av| 中文字幕亚洲专区| 久久国产精品久久精品| 亚洲欧美日韩爽爽影院| 国产成人精品免高潮在线观看| 亚洲国产美女精品久久久久∴| 亚洲成人性视频| 精品视频www| 久久全球大尺度高清视频| 97久久国产精品| 91亚洲精品一区| 日本久久亚洲电影| 久久全国免费视频| 亚洲一区中文字幕| 久热精品视频在线观看一区| 疯狂做受xxxx欧美肥白少妇| 亚洲国产精品系列| 日韩在线视频二区| 欧美三级欧美成人高清www| 久久久在线免费观看| 欧美黑人性视频| 欧美专区中文字幕| 久久久999国产精品| 色偷偷综合社区| 日韩精品欧美国产精品忘忧草| 精品国产美女在线| 欧美国产高跟鞋裸体秀xxxhd| 国产日韩换脸av一区在线观看| 国产精品久久国产精品99gif| 亚洲视频自拍偷拍| 久久青草精品视频免费观看| 在线视频日本亚洲性| 亚洲精品福利在线观看| 久久露脸国产精品| 亚洲免费电影在线观看| 68精品久久久久久欧美| 亚洲成人黄色在线观看| 国产精品女人久久久久久| 国产精品香蕉在线观看| 色哟哟入口国产精品| 136fldh精品导航福利| 亚洲精品成人免费| 国产成+人+综合+亚洲欧美丁香花| 伊人久久综合97精品| 欧美疯狂做受xxxx高潮| 亚洲激情在线观看| 亚洲成avwww人| 精品国产自在精品国产浪潮| 欧美影院在线播放| 成人中文字幕在线观看| 国产乱人伦真实精品视频| 中文字幕一区电影| 亚洲精品wwwww| 97视频在线观看视频免费视频| 欧美人与性动交| 一区二区日韩精品| 欧美电影在线观看网站| 久久精品91久久久久久再现| 欧洲美女7788成人免费视频| 欧美成人手机在线| 久久久欧美精品| 久久久电影免费观看完整版| 日韩视频中文字幕| 精品亚洲一区二区三区在线播放| 成人情趣片在线观看免费| 亚洲最大的av网站| 国产精品一区av| 欧美性一区二区三区| 最新亚洲国产精品| 日韩av免费在线观看| 国模叶桐国产精品一区| 亚洲va欧美va在线观看|