Angular算是將后端開發(fā)工程化引入前端的先驅(qū)之一,而Dependency injection依賴注入(后面簡稱為DI)又是Angular內(nèi)部運作的核心功能,所以要深入理解Angular有必要先理解這一核心概念鹤啡。
維基百科對依賴注入的解釋
在軟件工程中扣唱,依賴注入是實現(xiàn)控制反轉(zhuǎn)的一種軟件設(shè)計模式卦停,一個依賴是一個被其他對象(client)調(diào)用的對象(服務(wù)),注入則是將被依賴的對象(service)實例傳遞給依賴對象(client)的行為。將 被依賴的對象傳給依賴者,而不需要依賴者自己去創(chuàng)建或查找所需對象是DI的基本原則蜻拨。 依賴注入允許程序設(shè)計遵從依賴倒置原則(簡單的說就是要求對抽象進(jìn)行編程,不要對實現(xiàn)進(jìn)行編程桩引,這樣就降低了客戶與實現(xiàn)模塊間的耦合) 調(diào)用者(client)只需知道服務(wù)的接口缎讼,具體服務(wù)的查找和創(chuàng)建由注入者(injector)負(fù)責(zé)處理并提供給client,這樣就分離了服務(wù)和調(diào)用者的依賴坑匠,符合低耦合的程序設(shè)計原則血崭。
依賴注入中的角色
從維基百科解釋可知, DI中包含三個角色,調(diào)用者(client), 服務(wù)(service)和注入者 (injector),下面開始介紹本文的主題 Angular的依賴注入夹纫。
Angular依賴注入分析
先看看下面這段 hello,world代碼 (注意:設(shè)置了嚴(yán)格模式或壓縮混淆代碼后 下面的代碼不能正常工作,后面有解釋)
angular.module('myApp', [])
.controller('Ctl', function ($scope, $log) {
$scope.name = 'leonwgc';
$log.log('hello,world');
});
上面這段代碼就用到了angular的依賴注入咽瓷,代碼首先創(chuàng)建了一個myApp模塊,然后在此模塊中創(chuàng)建了Ctl控制器舰讹,創(chuàng)建控制器函數(shù)的第二個參數(shù)則是控制器的構(gòu)造函數(shù)茅姜, 構(gòu)造函數(shù)聲明了對$scope和$log服務(wù)的依賴。 當(dāng)構(gòu)造函數(shù)執(zhí)行時月匣, 即可獲得$scope和$log服務(wù)實例钻洒,進(jìn)行操作。 從我們前面對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 = [],
// 查詢依賴
$inject = createInjector.$$annotate(fn, strictDi, serviceName),
length, i,
key;
// 中間一段略去...
// 遍歷$inject數(shù)組調(diào)用getService獲取服務(wù)....
//開始執(zhí)行client , args則是依賴的全部服務(wù),injector都為我們創(chuàng)建好了
return fn.apply(self, args);
}
// 中間一段略去...
// 這里返回公開的injector對象
return {
// 執(zhí)行DI方法,比如上面的控制器函數(shù)
// invoke方法首先就是調(diào)用annotate取得依賴
// 然后調(diào)用get取得服務(wù)
// 如果緩存中沒有服務(wù)头遭,get內(nèi)部調(diào)用instantiate創(chuàng)建服務(wù)并緩存
// 最后利用function.apply傳入依賴并執(zhí)行
invoke: invoke,
// 實例化(創(chuàng)建)服務(wù)
instantiate: instantiate,
// 獲取服務(wù)(如果緩存中有,直接從緩存拿癣诱,沒有則調(diào)用instantiate創(chuàng)建并放入緩存,下次直接從緩存拿)
get: getService,
// 獲得依賴服務(wù)
annotate: createInjector.$$annotate,
// 檢查緩存中是否包含服務(wù)
has: function(name) {
return providerCache.hasOwnProperty(name + providerSuffix)
|| cache.hasOwnProperty(name);
}
};
}
源碼中查詢依賴的源碼如下:
function annotate(fn, strictDi, name) {
var $inject,
fnText,
argDecl,
last;
if (typeof fn === 'function') {
// 如果我們直接給函數(shù)添加了$inject依賴
// 則直接返回依賴计维,后面不做處理
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);
}
// 針對直接在構(gòu)造函數(shù)中使用服務(wù)的情況
// 使用function.toString() 然后正則匹配出依賴的對象
// 所以上面例子如果混淆了代碼就呵呵了
// 最后存入$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);
});
});
}
上海領(lǐng)思教育科技有限公司是一家致力于高素質(zhì)軟件開發(fā)人才培養(yǎng)的公司,一方面解決企業(yè)招不到優(yōu)秀人才 的困擾撕予,同時為優(yōu)秀的大學(xué)畢業(yè)生提供改變命運的機(jī)遇享潜。公司自成立以來,一直堅持采用“好老師+好學(xué)生+ 好學(xué)習(xí)氣氛”的培養(yǎng)模式嗅蔬,已經(jīng)培養(yǎng)了一批又一批的IT人才剑按。
上海領(lǐng)思期待您的加盟。
地址:上海市浦東新區(qū)臨港新城水蕓路300號501室
電話:021-58010107
網(wǎng)址:http://www.lingsiedu.cn
簡歷投遞:hr@lingsiedu.cn
//給構(gòu)造函數(shù)添加$inject屬性
fn.$inject = $inject;
}
} else if (isArray(fn)) {
last = fn.length - 1;
assertArgFn(fn[last], 'fn');
// 如果是數(shù)組格式澜术,則依賴對象是數(shù)組的第一個到倒數(shù)第二個對象
// 要調(diào)用的函數(shù)則是數(shù)組的最后一個元素
$inject = fn.slice(0, last);
} else {
assertArgFn(fn, 'fn', true);
}
// 返回依賴數(shù)組
return $inject;
}
看了上面的源碼片段和解釋艺蝴,想必大家對angular的依賴注入有了整體的認(rèn)識。
下面是另外兩種推薦的聲明依賴的方式
1. 數(shù)組注釋 (推薦), js壓縮混淆不會有影響鸟废。
angular.module('myApp', [])
.controller('Ctl', ['$scope', '$log', function ($scope, $log) {
$scope.name = 'leonwgc';
$log.log('hello,world');
}]);
2.$inject 屬性 ,js壓縮混淆不會有影響
angular.module('myApp', [])
.controller('Ctl', Ctrl);
function Ctrl($scope, $log) {
$scope.name = 'leonwgc';
$log.log('hello,world');
}
// 給構(gòu)造函數(shù)添加$inject屬性,
// $inject是一個數(shù)組猜敢,元素是依賴的服務(wù)名.
Ctrl.$inject = ["$scope", "$log"];