在上一篇《Angularjs開發一些經驗總結》中提到angular開發中的IOC inject優先,所以在這節將引用angularjs的注入代碼來分析angularjs的注入方式。在《再談angularjs DI(Dependency Injection)》中提到angularjs的注入方式分為3中方式,如果你還不清楚的請移步。
- 推斷式注入:在angularjs中我們可以在我們需要注入的地方按照名稱注入,這里要求參數名稱必須和注入服務實例名稱相同,一種名稱約定,angularjs會提取參數名稱查找相應DI實例注入;
- :標記注入:在angularjs中我們可以利用
$inject標注DI注入,這里需要注入服務名稱的順序和構造參數名對應.這里可以解決以上約定的死板性; - 內聯注入:對於directives,factory,filter等特殊指令使用$inject標注注入使用不是那么友好,angularjs特別增加了內聯注入。
在這里我們先上今天的主角code:
1 /** 2 * @ngdoc overview 3 * @name AUTO 4 * @description 5 * 6 * Implicit module which gets automatically added to each {@link AUTO.$injector $injector}. 7 */ 8 9 var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; 10 var FN_ARG_SPLIT = /,/; 11 var FN_ARG = /^\s*(_?)(.+?)\1\s*$/; 12 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; 13 function annotate(fn) { 14 var $inject, 15 fnText, 16 argDecl, 17 last; 18 19 if (typeof fn == 'function') { 20 if (!($inject = fn.$inject)) { 21 $inject = []; 22 fnText = fn.toString().replace(STRIP_COMMENTS, ''); 23 argDecl = fnText.match(FN_ARGS); 24 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ 25 arg.replace(FN_ARG, function(all, underscore, name){ 26 $inject.push(name); 27 }); 28 }); 29 fn.$inject = $inject; 30 } 31 } else if (isArray(fn)) { 32 last = fn.length - 1; 33 assertArgFn(fn[last], 'fn') 34 $inject = fn.slice(0, last); 35 } else { 36 assertArgFn(fn, 'fn', true); 37 } 38 return $inject; 39 }
在上面code中我們可夠很清晰的看到首先這里會判斷是不是一個'function',如果是的我們這會判斷fn.$inject為空,不為空則返回此注入(標記注入),為空則會根據正則匹配獲取參數列表,注入服務的名字按名注入;相反如果不是'function'而是Array的話獲取前n-1的服務名稱注入,這是內聯注入。
關於angularjs注入就到這里,在最后想說下說這代碼中我們能獲取的兩個javascript技巧:
- 對於javascript是運行時解釋的弱類型語言,我們無法利用c#,java類似的反射機制去獲取參數信息,我們只能靠正則從javascript代碼中匹配(從function.toString()).
- 正則的強大之處,在javascript中replace函數的強大應用:
arg.replace(FN_ARG, function(all, underscore, name){ $inject.push(name); });
關於這點可以參考《JavaScript 函數replace揭秘》。
