最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入理解Javascript里的依賴注入

 更新時(shí)間:2014年03月19日 11:52:13   作者:  
我喜歡引用這句話,“程序是對(duì)復(fù)雜性的管理”。計(jì)算機(jī)世界是一個(gè)巨大的抽象建筑群。我們簡(jiǎn)單的包裝一些東西然后發(fā)布新工具,周而復(fù)始?,F(xiàn)在思考下,你所使用的語(yǔ)言包括的一些內(nèi)建的抽象函數(shù)或是低級(jí)操作符。這在JavaScript里是一樣的


遲早你需要用到其他開發(fā)人員的抽象成果——即你依靠別人的代碼。我喜歡依賴自由(無(wú)依賴)的模塊,但那是難以實(shí)現(xiàn)的。甚至你創(chuàng)建的那些漂亮的黑盒子組件也或多或少會(huì)依賴一些東西。這正是依賴注入大顯身手的之處。現(xiàn)在有效地管理依賴的能力是絕對(duì)必要的。本文總結(jié)了我對(duì)問(wèn)題探索和一些的解決方案。

一、目標(biāo)
設(shè)想我們有兩個(gè)模塊。第一個(gè)是負(fù)責(zé)Ajax請(qǐng)求服務(wù)(service),第二個(gè)是路由(router)。

復(fù)制代碼 代碼如下:

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

我們有另一個(gè)函數(shù)需要用到這兩個(gè)模塊。
復(fù)制代碼 代碼如下:

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

為使看起來(lái)更有趣,這函數(shù)接受一個(gè)參數(shù)。當(dāng)然,我們完全可以使用上面的代碼,但這顯然不夠靈活。如果我們想使用ServiceXML或ServiceJSON呢,或者如果我們需要一些測(cè)試模塊呢。我們不能僅靠編輯函數(shù)體來(lái)解決問(wèn)題。首先,我們可以通過(guò)函數(shù)的參數(shù)來(lái)解決依賴性。即:
復(fù)制代碼 代碼如下:

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

我們通過(guò)傳遞額外的參數(shù)來(lái)實(shí)現(xiàn)我們想要的功能,然而,這會(huì)帶來(lái)新的問(wèn)題。想象如果我們的doSomething 方法散落在我們的代碼中。如果我們需要更改依賴條件,我們不可能更改所有調(diào)用函數(shù)的文件。

我們需要一個(gè)能幫我們搞定這些的工具。這就是依賴注入嘗試解決的問(wèn)題。讓我們寫下一些我們的依賴注入解決辦法應(yīng)該達(dá)到的目標(biāo):

我們應(yīng)該能夠注冊(cè)依賴關(guān)系
1.注入應(yīng)該接受一個(gè)函數(shù),并返回一個(gè)我們需要的函數(shù)
2.我們不能寫太多東西——我們需要精簡(jiǎn)漂亮的語(yǔ)法
3.注入應(yīng)該保持被傳遞函數(shù)的作用域
4.被傳遞的函數(shù)應(yīng)該能夠接受自定義參數(shù),而不僅僅是依賴描述
5.堪稱完美的清單,下面 讓我們實(shí)現(xiàn)它。
三、RequireJS / AMD的方法
你可能對(duì)RequireJS早有耳聞,它是解決依賴注入不錯(cuò)的選擇。

復(fù)制代碼 代碼如下:

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

這種想法是先描述需要的依賴,然后再寫你的函數(shù)。這里參數(shù)的順序很重要。如上所說(shuō),讓我們寫一個(gè)叫做injector的模塊,能接受相同的語(yǔ)法。
復(fù)制代碼 代碼如下:

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");
再繼續(xù)之前我應(yīng)該解釋清楚doSomething函數(shù)體內(nèi)容,我使用expect.js (斷言方面的庫(kù))僅是為了保證我寫的代碼的行為和我期望的是一樣的,體現(xiàn)一點(diǎn)點(diǎn)TDD(測(cè)試驅(qū)動(dòng)開發(fā))方法。
下面開始我們的injector模塊,這是非常棒的一個(gè)單例模式,所以它能在我們程序的不同部分工作的很好。
復(fù)制代碼 代碼如下:

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

    }
}


這是一個(gè)非常簡(jiǎn)單的對(duì)象,有兩個(gè)方法,一個(gè)用來(lái)存儲(chǔ)的屬性。我們要做的是檢查deps數(shù)組并在dependencies變量中搜索答案。剩下的只是調(diào)用.apply方法并傳遞之前的func方法的參數(shù)。
復(fù)制代碼 代碼如下:

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)));
    }       
}

scope是可選的,Array.prototype.slice.call(arguments, 0)是必須的,用來(lái)將arguments變量轉(zhuǎn)換為真正的數(shù)組。到目前為止還不錯(cuò)。我們的測(cè)試通過(guò)了。這種實(shí)現(xiàn)的問(wèn)題是,我們需要寫所需部件兩次,并且我們不能混淆他們的順序。附加的自定義參數(shù)總是位于依賴之后。

四、反射方法
根據(jù)維基百科的定義反射是指一個(gè)程序在運(yùn)行時(shí)檢查和修改一個(gè)對(duì)象的結(jié)構(gòu)和行為的能力。簡(jiǎn)單的說(shuō),在JavaScript的上下文里,這具體指讀取和分析的對(duì)象或函數(shù)的源代碼。讓我們完成文章開頭提到的doSomething函數(shù)。如果你在控制臺(tái)輸出doSomething.tostring()。你將得到如下的字符串:

復(fù)制代碼 代碼如下:

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

通過(guò)此方法返回的字符串給我們遍歷參數(shù)的能力,更重要的是,能夠獲取他們的名字。這其實(shí)是Angular 實(shí)現(xiàn)它的依賴注入的方法。我偷了一點(diǎn)懶,直接截取Angular代碼中獲取參數(shù)的正則表達(dá)式。
復(fù)制代碼 代碼如下:

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

我們可以像下面這樣修改resolve 的代碼:
復(fù)制代碼 代碼如下:

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);
    }       
}

我們執(zhí)行正則表達(dá)式的結(jié)果如下:
復(fù)制代碼 代碼如下:

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

看起來(lái),我們只需要第二項(xiàng)。一旦我們清楚空格并分割字符串就得到deps數(shù)組。只有一個(gè)大的改變:
復(fù)制代碼 代碼如下:

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

我們循環(huán)遍歷dependencies數(shù)組,如果發(fā)現(xiàn)缺失項(xiàng)則嘗試從arguments對(duì)象中獲取。謝天謝地,當(dāng)數(shù)組為空時(shí),shift方法只是返回undefined,而不是拋出一個(gè)錯(cuò)誤(這得益于web的思想)。新版的injector 能像下面這樣使用:
復(fù)制代碼 代碼如下:

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");

不必重寫依賴并且他們的順序可以打亂。它仍然有效,我們成功復(fù)制了Angular的魔法。

然而,這種做法并不完美,這就是反射類型注射一個(gè)非常大的問(wèn)題。壓縮會(huì)破壞我們的邏輯,因?yàn)樗淖儏?shù)的名字,我們將無(wú)法保持正確的映射關(guān)系。例如,doSometing()壓縮后可能看起來(lái)像這樣:

復(fù)制代碼 代碼如下:

var doSomething=function(e,t,n){var r=e();var i=t()}
Angular團(tuán)隊(duì)提出的解決方案看起來(lái)像:

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

}]);


這看起來(lái)很像我們開始時(shí)的解決方案。我沒(méi)能找到一個(gè)更好的解決方案,所以決定結(jié)合這兩種方法。下面是injector的最終版本。
復(fù)制代碼 代碼如下:

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訪客接受兩或三個(gè)參數(shù),如果有兩個(gè)參數(shù)它實(shí)際上和文章前面寫的一樣。然而,如果有三個(gè)參數(shù),它會(huì)將第一個(gè)參數(shù)轉(zhuǎn)換并填充deps數(shù)組,下面是一個(gè)測(cè)試?yán)樱?BR>
復(fù)制代碼 代碼如下:

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");

你可能注意到在第一個(gè)參數(shù)后面有兩個(gè)逗號(hào)——注意這不是筆誤??罩祵?shí)際上代表“Other”參數(shù)(占位符)。這顯示了我們是如何控制參數(shù)順序的。

五、直接注入Scope
有時(shí)我會(huì)用到第三個(gè)注入變量,它涉及到操作函數(shù)的作用域(換句話說(shuō),就是this對(duì)象)。所以,很多時(shí)候不需要使用這個(gè)變量。

復(fù)制代碼 代碼如下:

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

我們所做的一切其實(shí)就是將依賴添加到作用域。這樣做的好處是,開發(fā)人員不用再寫依賴性參數(shù);它們已經(jīng)是函數(shù)作用域的一部分。
復(fù)制代碼 代碼如下:

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

六、結(jié)束語(yǔ)
其實(shí)我們大部分人都用過(guò)依賴注入,只是我們沒(méi)有意識(shí)到。即使你不知道這個(gè)術(shù)語(yǔ),你可能在你的代碼里用到它百萬(wàn)次了。希望這篇文章能加深你對(duì)它的了解。

相關(guān)文章

  • JS實(shí)現(xiàn)常用導(dǎo)航鼠標(biāo)下經(jīng)過(guò)下方橫線自動(dòng)跟隨效果

    JS實(shí)現(xiàn)常用導(dǎo)航鼠標(biāo)下經(jīng)過(guò)下方橫線自動(dòng)跟隨效果

    這篇文章主要介紹了JS寫常用導(dǎo)航鼠標(biāo)下經(jīng)過(guò)下方橫線自動(dòng)跟隨效果,文中還給大家講解了基于css?+?js?實(shí)現(xiàn)導(dǎo)航欄下劃線跟隨鼠標(biāo)滑動(dòng)效果,需要的朋友可以參考下
    2023-01-01
  • 微信小程序發(fā)布新版本時(shí)自動(dòng)提示用戶更新的方法

    微信小程序發(fā)布新版本時(shí)自動(dòng)提示用戶更新的方法

    這篇文章主要介紹了微信小程序發(fā)布新版本時(shí)自動(dòng)提示用戶更新的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用小程序具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • innerHTML動(dòng)態(tài)添加html代碼和腳本兼容多個(gè)瀏覽器

    innerHTML動(dòng)態(tài)添加html代碼和腳本兼容多個(gè)瀏覽器

    innerHTML動(dòng)態(tài)添加html代碼和腳本,給某個(gè)元素的innerHTML賦值,并使得值中的js代碼有效且兼容多個(gè)瀏覽器,很棒的一個(gè)方法
    2014-10-10
  • JavaScript架構(gòu)前端不能沒(méi)有監(jiān)控系統(tǒng)原因

    JavaScript架構(gòu)前端不能沒(méi)有監(jiān)控系統(tǒng)原因

    這篇文章主要為大家介紹了為什么前端不能沒(méi)有監(jiān)控系統(tǒng)的原因,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 使用ionic切換頁(yè)面卡頓的解決方法

    使用ionic切換頁(yè)面卡頓的解決方法

    使用ionic開發(fā)app的時(shí)候,會(huì)發(fā)現(xiàn)切換頁(yè)面的動(dòng)畫會(huì)卡頓,并不流暢,為了保證用戶體驗(yàn),大家采用cordova提供的native transitions可以讓頁(yè)面切換近乎原型的體驗(yàn),一起通過(guò)本文學(xué)習(xí)下具體實(shí)現(xiàn)思路
    2016-12-12
  • JS實(shí)現(xiàn)顏色梯度與漸變效果完整實(shí)例

    JS實(shí)現(xiàn)顏色梯度與漸變效果完整實(shí)例

    這篇文章主要介紹了JS實(shí)現(xiàn)顏色梯度與漸變效果,結(jié)合完整實(shí)例形式分析了js顏色漸變所涉及的數(shù)學(xué)運(yùn)算與頁(yè)面樣式動(dòng)態(tài)操作相關(guān)技巧,需要的朋友可以參考下
    2016-12-12
  • JavaScript簡(jiǎn)單下拉菜單實(shí)例代碼

    JavaScript簡(jiǎn)單下拉菜單實(shí)例代碼

    這篇文章主要介紹了JavaScript簡(jiǎn)單下拉菜單,采用JavaScript定時(shí)函數(shù)遞歸調(diào)用實(shí)現(xiàn)菜單漸顯效果,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下
    2015-09-09
  • JavaScript 如何在線解壓 ZIP 文件

    JavaScript 如何在線解壓 ZIP 文件

    本文將介紹瀏覽器解壓和服務(wù)器解壓兩種在線解壓ZIP文件的方案。幫助大家用JavaScript實(shí)現(xiàn)在線解壓,感興趣的朋友可以參考下
    2021-05-05
  • JavaScript刪除數(shù)組元素的方法指南

    JavaScript刪除數(shù)組元素的方法指南

    作為一名前端開發(fā)工程師,我們經(jīng)常需要在 JavaScript 中操作數(shù)組,其中比較常見(jiàn)的操作便是對(duì)數(shù)組進(jìn)行元素的添加、刪除和修改。在這篇文章中,我會(huì)詳細(xì)介紹JS中所有刪除數(shù)組元素的方法,希望對(duì)大家有所幫助
    2023-05-05
  • JavaScript prototype 使用介紹

    JavaScript prototype 使用介紹

    用過(guò)JavaScript的同學(xué)們肯定都對(duì)prototype如雷貫耳,但是這究竟是個(gè)什么東西卻讓初學(xué)者莫衷一是,只知道函數(shù)都會(huì)有一個(gè)prototype屬性,可以為其添加函數(shù)供實(shí)例訪問(wèn),其它的就不清楚了,最近看了一些 JavaScript高級(jí)程序設(shè)計(jì),終于揭開了其神秘面紗
    2013-08-08

最新評(píng)論

昌宁县| 那坡县| 大余县| 桑日县| 桃江县| 察雅县| 姜堰市| 冷水江市| 金门县| 郑州市| 广饶县| 白银市| 宁国市| 合阳县| 乌兰县| 阿荣旗| 东乌| 普兰店市| 江孜县| 保德县| 合江县| 会宁县| 乡宁县| 福建省| 武陟县| 永善县| 申扎县| 湛江市| 巴塘县| 达拉特旗| 霍邱县| 大兴区| 宣武区| 库车县| 恩平市| 南平市| 若羌县| 利辛县| 西城区| 许昌市| 清水县|