Angular依賴(lài)注入詳解

Angular算是將后端開(kāi)發(fā)工程化引入前端的先驅(qū)之一,而Dependency injection依賴(lài)注入(后面簡(jiǎn)稱(chēng)為DI)又是Angular內(nèi)部運(yùn)作的核心功能,所以要深入理解Angular有必要先理解這一核心概念。

維基百科對(duì)依賴(lài)注入的解釋?zhuān)?/p>

在軟件工程中,依賴(lài)注入是實(shí)現(xiàn)控制反轉(zhuǎn)的一種軟件設(shè)計(jì)模式,一個(gè)依賴(lài)是一個(gè)被其他對(duì)象(client)調(diào)用的對(duì)象(服務(wù)),注入則是將被依賴(lài)的對(duì)象(service)實(shí)例傳遞給依賴(lài)對(duì)象(client)的行為。將被依賴(lài)的對(duì)象傳給依賴(lài)者,而不需要依賴(lài)者自己去創(chuàng)建或查找所需對(duì)象是DI的基本原則。 依賴(lài)注入允許程序設(shè)計(jì)遵從依賴(lài)倒置原則(簡(jiǎn)單的說(shuō)就是要求對(duì)抽象進(jìn)行編程,不要對(duì)實(shí)現(xiàn)進(jìn)行編程,這樣就降低了客戶與實(shí)現(xiàn)模塊間的耦合) 調(diào)用者(client)只需知道服務(wù)的接口,具體服務(wù)的查找和創(chuàng)建由注入者(injector)負(fù)責(zé)處理并提供給client,這樣就分離了服務(wù)和調(diào)用者的依賴(lài),符合低耦合的程序設(shè)計(jì)原則。

依賴(lài)注入中的角色

從維基百科解釋可知, DI中包含三個(gè)角色,調(diào)用者(client), 服務(wù)(service)和注入者 (injector),下面開(kāi)始介紹本文的主題 Angular的依賴(lài)注入。

Angular依賴(lài)注入分析

先看看下面這段 hello,world代碼 (注意:設(shè)置了嚴(yán)格模式或壓縮混淆代碼后 下面的代碼不能正常工作,后面有解釋?zhuān)?/p>

angular.module('myApp', [])

.controller('Ctl', function ($scope, $log) {

$scope.name = 'leonwgc';

$log.log('hello,world');

});

上面這段代碼就用到了angular的依賴(lài)注入,代碼首先創(chuàng)建了一個(gè)myApp模塊,然后在此模塊中創(chuàng)建了Ctl控制器,創(chuàng)建控制器函數(shù)的第二個(gè)參數(shù)則是控制器的構(gòu)造函數(shù),構(gòu)造函數(shù)聲明了對(duì)$scope和$log服務(wù)的依賴(lài)。 當(dāng)構(gòu)造函數(shù)執(zhí)行時(shí), 即可獲得$scope和$log服務(wù)實(shí)例,進(jìn)行操作。 從我們前面對(duì)DI的了解,

$scope和$log是由注入器injector 提供,知道了injector的存在,我們直接從

angular的源碼中將其找出,如下:

function createInternalInjector(cache, factory) {

// 中間一段略去...

// 調(diào)用client

function invoke(fn, self, locals, serviceName) {

if (typeof locals === 'string') {

serviceName = locals;

locals = null;

}

var args = [],

// 查詢(xún)依賴(lài)

$inject = createInjector.$$annotate(fn, strictDi, serviceName),

length, i,

key;

// 為省手機(jī)流量中間一段略去...

// 遍歷$inject數(shù)組調(diào)用getService獲取服務(wù)....

//開(kāi)始執(zhí)行client , args則是依賴(lài)的全部服務(wù),injector都為我們創(chuàng)建好了

return fn.apply(self, args);

}

// 中間一段略去...

// 這里返回公開(kāi)的injector對(duì)象

return {

// 執(zhí)行DI方法,比如上面的控制器函數(shù)

// invoke方法首先就是調(diào)用annotate取得依賴(lài)

// 然后調(diào)用get取得服務(wù)

// 如果緩存中沒(méi)有服務(wù),get內(nèi)部調(diào)用instantiate創(chuàng)建服務(wù)并緩存

// 最后利用function.apply傳入依賴(lài)并執(zhí)行

invoke: invoke,

// 實(shí)例化(創(chuàng)建)服務(wù)

instantiate: instantiate,

// 獲取服務(wù)(如果緩存中有,直接從緩存拿,沒(méi)有則調(diào)用instantiate創(chuàng)建并放入緩存,下次直接從緩存拿)

get: getService,

// 獲得依賴(lài)服務(wù)

annotate: createInjector.$$annotate,

// 檢查緩存中是否包含服務(wù)

has: function(name) {

return providerCache.hasOwnProperty(name + providerSuffix)

|| cache.hasOwnProperty(name);

}

};

}

源碼中查詢(xún)依賴(lài)的源碼如下:

function annotate(fn, strictDi, name) {

var $inject,

fnText,

argDecl,

last;

if (typeof fn === 'function') {

// 如果我們直接給函數(shù)添加了$inject依賴(lài)

// 則直接返回依賴(lài),后面不做處理

if (!($inject = fn.$inject)) {

$inject = [];

if (fn.length) {

if (strictDi) {

if (!isString(name) || !name) {

name = fn.name || anonFn(fn);

}

throw $injectorMinErr('strictdi',

'{0} is not using explicit annotation...', name);

}

// 針對(duì)直接在構(gòu)造函數(shù)中使用服務(wù)的情況

// 使用function.toString() 然后正則匹配出依賴(lài)的對(duì)象

// 所以上面例子如果混淆了代碼就呵呵了

// 最后存入$inject數(shù)組

fnText = fn.toString().replace(STRIP_COMMENTS, '');

argDecl = fnText.match(FN_ARGS);

forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {

arg.replace(FN_ARG, function(all, underscore, name) {

$inject.push(name);

});

});

}

//給構(gòu)造函數(shù)添加$inject屬性

fn.$inject = $inject;

}

} else if (isArray(fn)) {

last = fn.length - 1;

assertArgFn(fn[last], 'fn');

// 如果是數(shù)組格式,則依賴(lài)對(duì)象是數(shù)組的第一個(gè)到倒數(shù)第二個(gè)對(duì)象

// 要調(diào)用的函數(shù)則是數(shù)組的最后一個(gè)元素

$inject = fn.slice(0, last);

} else {

assertArgFn(fn, 'fn', true);

}

// 返回依賴(lài)數(shù)組

return $inject;

}

看了上面的源碼片段和解釋?zhuān)氡卮蠹覍?duì)angular的依賴(lài)注入有了整體的認(rèn)識(shí)。

下面是另外兩種推薦的聲明依賴(lài)的方式:

1. 數(shù)組注釋 (推薦), js壓縮混淆不會(huì)有影響。

angular.module('myApp', [])

.controller('Ctl', ['$scope', '$log', function ($scope, $log) {

$scope.name = 'leonwgc';

$log.log('hello,world');

}]);

2.$inject 屬性 ,js壓縮混淆不會(huì)有影響

angular.module('myApp', [])

.controller('Ctl', Ctrl);

function Ctrl($scope, $log) {

$scope.name = 'leonwgc';

$log.log('hello,world');

}

// 給構(gòu)造函數(shù)添加$inject屬性,

// $inject是一個(gè)數(shù)組,元素是依賴(lài)的服務(wù)名.

Ctrl.$inject = ["$scope", "$log"];

Angular引入了大量后端開(kāi)發(fā)的概念,而前端同學(xué)可能還不熟悉,望本文能有所幫助。


本文作者:汪國(guó)超(點(diǎn)融黑幫),點(diǎn)融網(wǎng)前端開(kāi)發(fā),對(duì)新技術(shù)尤其是前端新技術(shù)特別有興趣,吃貨一枚, 喜歡旅游看美劇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Angular面試題 一、ng-show/ng-hide與ng-if的區(qū)別? 第一點(diǎn)區(qū)別是,ng-if在后面表達(dá)式...
    w_zhuan閱讀 5,708評(píng)論 0 26
  • 1、angularjs的幾大特性是什么? 雙向數(shù)據(jù)綁定、依賴(lài)注入、模板、指令、MVC/MVVM 2、列舉幾種常見(jiàn)的...
    2e9a10d418ab閱讀 1,464評(píng)論 0 10
  • 1、angularjs的幾大特性是什么? 雙向數(shù)據(jù)綁定、依賴(lài)注入、模板、指令、MVC/MVVM 2、列舉幾種常見(jiàn)的...
    秀才JaneBook閱讀 1,607評(píng)論 0 22
  • 1.類(lèi)庫(kù)( 提供類(lèi)方法 ) 和框架 類(lèi)庫(kù)提供一系列的函數(shù)和方法的合集,能夠加快你寫(xiě)代碼的速度。但是主導(dǎo)邏輯的還是自...
    w_zhuan閱讀 1,948評(píng)論 0 8
  • 一場(chǎng)說(shuō)走就走又蓄謀已久的海邊之旅。 A+2娃,B+1娃,C+1娃,D+2娃,ABCD四個(gè)女人攜六個(gè)萌娃出發(fā)啦。一行...
    小_俊閱讀 535評(píng)論 0 2

友情鏈接更多精彩內(nèi)容