標籤:
依賴注入是一種軟體設計模式,用來處理代碼的依賴關係。
一般來說有三種方法讓函數獲得它需要的依賴:
它的依賴是能被建立的,一般用new操作符就行。
能夠通過全域變數尋找依賴。
依賴能在需要時被匯入。
前兩種方式都不是很好,因為它們需要對依賴寫入程式碼,使得修改依賴的時候變得困難。特別是在測試的時候不好辦,因為對某個部分進行孤立的測試常常需要類比它的依賴。
第三種方式是最好的,因為它不必在組件中去主動尋找和擷取依賴,而是由外界將依賴傳入。
舉個例子:
function SomeClass(greeter) { this.greeter = greeter}SomeClass.prototype.doSomething = function(name) { this.greeter.greet(name);}
上面的例子中SomeClass不關心去哪裡獲得叫greeter的依賴。這是我們想要的結果,但是這也同時把擷取依賴的任務交給了負責建立SomeClass的代碼。
每一個AngularJS應用都有一個注入器(injector)用來處理依賴的建立。注入器是一個負責尋找和建立依賴的服務定位器。舉個例子:
angular.module(‘myModule‘, []). // 告訴注入器如何來建立這個greeter // 注意這個greeter本身依賴‘$window‘ factory(‘greeter‘, function($window) { // 這個方法,我們叫做Factory 方法,它的作用是用來建立這個greeter服務 return { greet: function(text) { $window.alert(text); } }; })// 在模組myModule中,建立一個新的注入器// (這經常在angular啟動時自動完成)var injector = angular.injector(‘myModule‘);// 從這個注入器中得到greeter這個服務var greeter = injector.get(‘greeter‘);
通過請求依賴的方式解決了硬式編碼問題,但是同樣也意味著注入器需要在應用中傳遞,這違反了迪米特法則。我們通過使用下面這個例子中聲明的方式來將依賴尋找都給注入器來解決。
<!-- Given this HTML --><div ng-controller="MyController"> <button ng-click="sayHello()">Hello</button></div>// And this controller definitionfunction MyController($scope, greeter) { $scope.sayHello = function() { greeter(‘Hello World‘); };}//‘ng-controller‘指令做了以下事情injector.instantiate(MyController);
注意,通過使用ng-controller來執行個體化控制器類,是的,控制器和注入器不再相關聯,這是最好的結果。控制器中的代碼可以簡單的請求依賴而不必處理注入器。這種方式就沒有破壞迪米特法則。
注入器怎麼知道需要注入什麼依賴呢?
注入器需要應用提供一些標記來表示自己需要的依賴。在關於AngularJS的某些API文檔中你會看到函數都是被注入器調用的。注入器需要知道函數需要什麼依賴。下面有三個等效的表示自己需要的依賴的方法。這些方法可以互相替換,並且是等效的。
(1)最簡單的處理依賴的方法,就是假設函數的參數名就是依賴的名字
function MyController($scope, greeter) { ...}
給出一個注入器,可以通過檢查函式宣告來擷取函數名,從而知道需要依賴的函數。在上面的例子中,$scope和greeter是需要注入到函數中的依賴。
坦白的來講,用了這種方法就不能使用JavaScript minifiers/obfuscators(一些用來縮短JS的類庫)了,因為它們會改變變數名。
(2)要允許壓縮類庫重新命名函數的參數,同時注入器又能正確處理依賴的話,函數需要使用$inject屬性。這個屬性是一個包含依賴的名稱的數組。
var MyController = function(renamed$scope, renamedGreeter) { ...}MyController.$inject = [‘$scope‘, ‘greeter‘];
注意$inject標記裡的值和函式宣告的參數是對應的。
這種方式適合用於控制器的聲明,因為控制器有了明確的聲明標記。
(3)有時候用$inject標記不是很方便,比如用來聲明指令的時候。
使用$inject會導致代碼膨脹:
var greeterFactory = function(renamed$window) { ...;};greeterFactory.$inject = [‘$window‘];someModule.factory(‘greeter‘, greeterFactory);
這種情況我們就推薦使用第三種方式:
someModule.factory(‘greeter‘, [‘$window‘, function(renamed$window) { ...;}]);
依賴注入在AngularJS中很普遍,一般用在控制器和Factory 方法中。
控制器中的依賴注入:
控制器是負責應用行為的類。推薦的控制器聲明方法如下:
var MyController = function(dep1, dep2) { ...}MyController.$inject = [‘dep1‘, ‘dep2‘];MyController.prototype.aMethod = function() { ...}
Factory 方法的依賴注入:
Factory 方法負責建立AngularJS中的大部分對象。比如指令,服務,過濾器。Factory 方法一般在模組中使用,推薦的方法如下:
angular.module(‘myModule‘, []). config([‘depProvider‘, function(depProvider){ ... }]). factory(‘serviceId‘, [‘depService‘, function(depService) { ... }]). directive(‘directiveName‘, [‘depService‘, function(depService) { ... }]). filter(‘filterName‘, [‘depService‘, function(depService) { ... }]). run([‘depService‘, function(depService) { ... }]);
加油!
AngularJS開發指南10:AngularJS依賴注入的詳解