国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

javascript中的依賴(lài)注入【轉(zhuǎn)】

ZHAO_ / 2795人閱讀

摘要:本來(lái)打算寫(xiě)篇文章介紹下控制反轉(zhuǎn)的常見(jiàn)模式依賴(lài)注入。這就是依賴(lài)注入起作用的地方,當(dāng)前來(lái)看,高效管理依賴(lài)的能力是迫切需要的,本文總結(jié)了原作者對(duì)這個(gè)問(wèn)題的看法。結(jié)束語(yǔ)依賴(lài)注入是我們所有人都做過(guò)的事情中的一種,可能沒(méi)有意識(shí)到罷了。

本來(lái)打算寫(xiě)篇文章介紹下控制反轉(zhuǎn)的常見(jiàn)模式-依賴(lài)注入。在翻看資料的時(shí)候,發(fā)現(xiàn)了一篇好文Dependency injection in JavaScript,就不自己折騰了,結(jié)合自己理解翻譯一下,好文共賞。

我喜歡引用這樣一句話‘編程是對(duì)復(fù)雜性的管理’。可能你也聽(tīng)過(guò)計(jì)算機(jī)世界是一個(gè)巨大的抽象結(jié)構(gòu)。我們簡(jiǎn)單的包裝東西并重復(fù)的生產(chǎn)新的工具。思考那么一下下,我們使用的編程語(yǔ)言都包括內(nèi)置的功能,這些功能可能是基于其他低級(jí)操作的抽象方法,包括我們是用的javascript。
遲早,我們都會(huì)需要使用別的開(kāi)發(fā)者開(kāi)發(fā)的抽象功能,也就是我們要依賴(lài)其他人的代碼。
我希望使用沒(méi)有依賴(lài)的模塊,顯然這是很難實(shí)現(xiàn)的。即使你創(chuàng)建了很好的像黑盒一樣的組件,但總有個(gè)將所有部分合并起來(lái)的地方。
這就是依賴(lài)注入起作用的地方,當(dāng)前來(lái)看,高效管理依賴(lài)的能力是迫切需要的,本文總結(jié)了原作者對(duì)這個(gè)問(wèn)題的看法。

目標(biāo)

假設(shè)我們有兩個(gè)模塊,一個(gè)是發(fā)出ajax請(qǐng)求的服務(wù),一個(gè)是路由:

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

下面是另一個(gè)依賴(lài)了上述模塊的函數(shù):

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

為了更有趣一點(diǎn),該函數(shù)需要接受一個(gè)參數(shù)。當(dāng)然我們可以使用上面的代碼,但是這不太靈活。
如果我們想使用ServiceXML、ServiceJSON,或者我們想要mock一些測(cè)試模塊,這樣我們不能每次都是編輯函數(shù)體。
為了解決這個(gè)現(xiàn)狀,首先我們提出將依賴(lài)當(dāng)做參數(shù)傳給函數(shù),如下:

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

這樣,我們把需要的模塊的具體實(shí)例傳遞過(guò)來(lái)。
然而這樣有個(gè)新的問(wèn)題:想一下如果dosomething函數(shù)在很多地方被調(diào)用,如果有第三個(gè)依賴(lài)條件,我們不能改變所有的調(diào)用doSomething的地方。
舉個(gè)小栗子:
假如我們有很多地方用到了doSomething:

//a.js
var a = doSomething(service,router,1)
//b.js 
var b = doSomething(service,router,2)
// 假如依賴(lài)條件更改了,即doSomething需要第三個(gè)依賴(lài),才能正常工作
// 這時(shí)候就需要在上面不同文件中修改了,如果文件數(shù)量夠多,就不合適了。
var doSomething = function(service, router, third,thother) {
    var s = service();
    var r = router();
    //***
};

因此,我們需要一個(gè)幫助我們來(lái)管理依賴(lài)的工具。這就是依賴(lài)注入器想要解決的問(wèn)題,先看一下我們想要達(dá)到的目標(biāo):

可以注冊(cè)依賴(lài)
注入器應(yīng)該接受一個(gè)函數(shù)并且返回一個(gè)已經(jīng)獲得需要資源的函數(shù)
我們不應(yīng)該寫(xiě)復(fù)雜的代碼,需要簡(jiǎn)短優(yōu)雅的語(yǔ)法
注入器應(yīng)該保持傳入函數(shù)的作用域
被傳入的函數(shù)應(yīng)該可以接受自定義參數(shù),不僅僅是被描述的依賴(lài)。
看起來(lái)比較完美的列表就如上了,讓我們來(lái)嘗試實(shí)現(xiàn)它。

requirejs/AMD的方式
大家都可能聽(tīng)說(shuō)過(guò)requirejs,它是很不錯(cuò)的依賴(lài)管理方案。

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

這種思路是首先聲明需要的依賴(lài),然后開(kāi)始編寫(xiě)函數(shù)。這里參數(shù)的順序是很重要的。我們來(lái)試試寫(xiě)一個(gè)名為injector的模塊,可以接受相同語(yǔ)法。

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的函數(shù)體,使用expect.js來(lái)作為斷言庫(kù)來(lái)確保我的代碼能像期望那樣正常工作。體現(xiàn)了一點(diǎn)點(diǎn)TDD(測(cè)試驅(qū)動(dòng)開(kāi)發(fā))的開(kāi)發(fā)模式。

下面是我們injector模塊的開(kāi)始,一個(gè)單例模式是很好的選擇,因此可以在我們應(yīng)用的不同部分運(yùn)行的很不錯(cuò)。

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

    }
}

從代碼來(lái)看,確實(shí)是一個(gè)很簡(jiǎn)單的對(duì)象。有兩個(gè)函數(shù)和一個(gè)作為存儲(chǔ)隊(duì)列的變量。
我們需要做的是檢查deps依賴(lài)數(shù)組,并且從dependencies隊(duì)列中查找答案。剩下的就是調(diào)用.apply方法來(lái)拼接被傳遞過(guò)來(lái)函數(shù)的參數(shù)。

//處理之后將依賴(lài)項(xiàng)當(dāng)做參數(shù)傳入給func

resolve: function(deps, func, scope) {
    var args = [];
    //處理依賴(lài),如果依賴(lài)隊(duì)列中不存在對(duì)應(yīng)的依賴(lài)模塊,顯然該依賴(lài)不能被調(diào)用那么報(bào)錯(cuò),
    for(var i=0; i

如果scope存在,是可以被有效傳遞的。Array.prototype.slice.call(arguments, 0)將arguments(類(lèi)數(shù)組)轉(zhuǎn)換成真正的數(shù)組。
目前來(lái)看很不錯(cuò)的,可以通過(guò)測(cè)試。當(dāng)前的問(wèn)題時(shí),我們必須寫(xiě)兩次需要的依賴(lài),并且順序不可變動(dòng),額外的參數(shù)只能在最后面。

反射實(shí)現(xiàn)

從維基百科來(lái)說(shuō),反射是程序在運(yùn)行時(shí)可以檢查和修改對(duì)象結(jié)構(gòu)和行為的一種能力。
簡(jiǎn)而言之,在js的上下文中,是指讀取并且分析對(duì)象或者函數(shù)的源碼。看下開(kāi)頭的doSomething,如果使用doSomething.toString() 可以得到下面的結(jié)果。

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

這種將函數(shù)轉(zhuǎn)成字符串的方式賦予我們獲取預(yù)期參數(shù)的能力。并且更重要的是,他們的name。
下面是Angular依賴(lài)注入的實(shí)現(xiàn)方式,我從Angular那拿了點(diǎn)可以獲取arguments的正則表達(dá)式:

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

這樣我們可以修改resolve方法了:

tip

這里,我將測(cè)試?yán)幽蒙蟻?lái)應(yīng)該更好理解一點(diǎn)。

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

繼續(xù)來(lái)看我們的實(shí)現(xiàn)。

resolve: function() {
    // agrs 傳給func的參數(shù)數(shù)組,包括依賴(lài)模塊及自定義參數(shù)
    var func, deps, scope, args = [], self = this;
    // 獲取傳入的func,主要是為了下面來(lái)拆分字符串
    func = arguments[0];
    // 正則拆分,獲取依賴(lài)模塊的數(shù)組
    deps = func.toString().match(/^functions*[^(]*(s*([^)]*))/m)[1].replace(/ /g, "").split(",");
    //待綁定作用域,不存在則不指定
    scope = arguments[1] || {};
    return function() {
        // 將arguments轉(zhuǎn)為數(shù)組
        // 即后面再次調(diào)用的時(shí)候,doSomething("Other");   
        // 這里的Other就是a,用來(lái)補(bǔ)充缺失的模塊。
        var a = Array.prototype.slice.call(arguments, 0);
        //循環(huán)依賴(lài)模塊數(shù)組
        for(var i=0; i

使用這個(gè)正則來(lái)處理函數(shù)時(shí),可以得到下面結(jié)果:

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

我們需要的只是第二項(xiàng),一旦我們清除數(shù)組并拆分字符串,我們將會(huì)得到依賴(lài)數(shù)組。主要變化在下面:

var a = Array.prototype.slice.call(arguments, 0);

...

args.push(self.dependencies[d] && d != "" ? self.dependencies[d] : a.shift());

這樣我們就循環(huán)遍歷依賴(lài)項(xiàng),如果缺少某些東西,我們可以嘗試從arguments對(duì)象中獲取。
幸好,當(dāng)數(shù)組為空的時(shí)候shift方法也只是返回undefined而非拋錯(cuò)。所以新版的用法如下:

//不用在前面聲明依賴(lài)模塊了

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ù)聲明了,順序也可變。我們復(fù)制了Angular的魔力。
然而,這并不完美,壓縮會(huì)破壞我們的邏輯,這是反射注入的一大問(wèn)題。因?yàn)閴嚎s改變了參數(shù)的名稱(chēng)所以我們沒(méi)有能力去解決這些依賴(lài)。例如:

// 顯然根據(jù)key來(lái)匹配就是有問(wèn)題的了

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

Angular團(tuán)隊(duì)的解決方案如下:

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

}]);

看起來(lái)就和開(kāi)始的require.js的方式一樣了。作者個(gè)人不能找到更優(yōu)的解決方案,為了適應(yīng)這兩種方式。最終方案看起來(lái)如下:

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(/^functions*[^(]*(s*([^)]*))/m)[1].replace(/ /g, "").split(",");
            scope = arguments[1] || {};
        }
        return function() {
            var a = Array.prototype.slice.call(arguments, 0);
            for(var i=0; i

現(xiàn)在resolve接受兩或者三個(gè)參數(shù),如果是兩個(gè)就是我們寫(xiě)的第一種了,如果是三個(gè),會(huì)將第一個(gè)參數(shù)解析并填充到deps。
下面就是測(cè)試?yán)?我一直認(rèn)為將這段例子放在前面可能大家更好閱讀一些。):

// 缺失了一項(xiàng)模塊other
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");
});
// 這里傳的Other將會(huì)用來(lái)拼湊
doSomething("Other");

可能會(huì)注意到argumets[0]中確實(shí)了一項(xiàng),就是為了測(cè)試填充功能的。

直接注入作用域
有時(shí)候,我們使用第三種的注入方式,它涉及到函數(shù)作用域的操作(或者其他名字,this對(duì)象),并不經(jīng)常使用

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

我們做的就是將依賴(lài)加到作用域上,這樣的好處是不用再參數(shù)里加依賴(lài)了,已經(jīng)是函數(shù)作用域的一部分了。

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ǔ)
依賴(lài)注入是我們所有人都做過(guò)的事情中的一種,可能沒(méi)有意識(shí)到罷了。即使沒(méi)有聽(tīng)過(guò),你也可能用過(guò)很多次了。
通過(guò)這篇文章對(duì)于這個(gè)熟悉而又陌生的概念的了解加深了不少,希望能幫助到有需要的同學(xué)。最后個(gè)人能力有限,翻譯有誤的地方歡迎大家指出,共同進(jìn)步。
再次感謝原文作者原文地址

如水穿石,厚積才可薄發(fā)

原文鏈接:https://www.cnblogs.com/pqjwy...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/104173.html

相關(guān)文章

  • JavaScript 依賴(lài)管理

    摘要:但是,由于天生存在著一點(diǎn)戲劇性據(jù)傳說(shuō)是在飛機(jī)上幾天時(shí)間設(shè)計(jì)出來(lái)的,模塊系統(tǒng)作為一門(mén)語(yǔ)言最基本的屬性卻是所缺的。尤其是在多頁(yè)面的項(xiàng)目下,不同頁(yè)面的腳本都是根據(jù)依賴(lài)關(guān)系異步按需加載的,不用手動(dòng)處理每個(gè)頁(yè)面加載腳本的情況。 轉(zhuǎn)原文 概述 javaScript — 目錄最火熱的語(yǔ)言,到處發(fā)著光芒, html5, hybrid apps, node.js, full-stack 等等。jav...

    kevin 評(píng)論0 收藏0
  • 常見(jiàn)六大Web 安全攻防解析

    摘要:想閱讀更多優(yōu)質(zhì)原創(chuàng)文章請(qǐng)猛戳博客一,跨站腳本攻擊,因?yàn)榭s寫(xiě)和重疊,所以只能叫。跨站腳本攻擊是指通過(guò)存在安全漏洞的網(wǎng)站注冊(cè)用戶的瀏覽器內(nèi)運(yùn)行非法的標(biāo)簽或進(jìn)行的一種攻擊。跨站腳本攻擊有可能造成以下影響利用虛假輸入表單騙取用戶個(gè)人信息。 前言 在互聯(lián)網(wǎng)時(shí)代,數(shù)據(jù)安全與個(gè)人隱私受到了前所未有的挑戰(zhàn),各種新奇的攻擊技術(shù)層出不窮。如何才能更好地保護(hù)我們的數(shù)據(jù)?本文主要側(cè)重于分析幾種常見(jiàn)的攻擊的類(lèi)型...

    lidashuang 評(píng)論0 收藏0
  • JavaScript 到 TypeScript - 模塊化和構(gòu)建

    摘要:不過(guò),相對(duì)于靜態(tài)類(lèi)型檢查帶來(lái)的好處,這些代價(jià)是值得的。當(dāng)然少不了的模塊化標(biāo)準(zhǔn),雖然到目前為止和大部分瀏覽器都還不支持它。本身支持兩種模塊化方式,一種是對(duì)的模塊的微小擴(kuò)展,另一種是在發(fā)布之前本身模仿的命名空間。有一種情況例外。 TypeScript 帶來(lái)的最大好處就是靜態(tài)類(lèi)型檢查,所以在從 JavaScript 轉(zhuǎn)向 TypeScript 之前,一定要認(rèn)識(shí)到添加類(lèi)型定義會(huì)帶來(lái)額外的工作量...

    Jonathan Shieber 評(píng)論0 收藏0
  • 前端面試--網(wǎng)絡(luò)

    摘要:從前發(fā)送請(qǐng)求后需等待并收到響應(yīng),才能發(fā)送下一個(gè)請(qǐng)求。第二次握手接收到包后就返回一個(gè),隨機(jī)數(shù),包以及一個(gè)自己的包,然后等待的回復(fù),進(jìn)入狀態(tài)已接收狀態(tài)。 1.Http協(xié)議 1.1 Http結(jié)構(gòu)圖 showImg(https://segmentfault.com/img/bVbsrzu?w=1240&h=702); 1.2 什么是Http協(xié)議 HTTP協(xié)議是Hyper Text Transf...

    calx 評(píng)論0 收藏0
  • xss注入實(shí)踐和防范

    摘要:劫持用戶是最常見(jiàn)的跨站攻擊形式,通過(guò)在網(wǎng)頁(yè)中寫(xiě)入并執(zhí)行腳本執(zhí)行文件多數(shù)情況下是腳本代碼,劫持用戶瀏覽器,將用戶當(dāng)前使用的信息發(fā)送至攻擊者控制的網(wǎng)站或服務(wù)器中。預(yù)防將錄入的惡意標(biāo)簽進(jìn)行轉(zhuǎn)碼再存儲(chǔ),主要在后端錄入的時(shí)候做。 xss定義 Cross Site Scripting的縮寫(xiě)本來(lái)是CSS,但是這樣就跟Cascading Style Sheets的縮寫(xiě)混淆了,所以使用XSS,使用字母X...

    Rango 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

ZHAO_

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<