angular需要對用戶的傳入函數進行靜態分析,抽取當中的依賴,才能工作。因此用戶的函數,包括控制器函數,工廠函數,服務函數與$watch回調都只是一個模板,用於取toString,真正運行的是編譯后的動態函數,有函數必須傳參。比如:
function TestCtrl($scope){ $scope.name = "xxx" }
$scope就是一個很復雜的類的實例,angular內部有許多類,如何決定是放這個類的實例而不是其他類的呢。這就要看這參數長得什么樣子,比如$scope肯定是作用域對象,$timeout就是定時器的。angular稱之為依賴注入,而這個最簡單的注入叫做推斷式注入。實現很簡單,取toString, 去掉函數名,去掉函數體,去掉參數名之間的注釋與逗號,剩下就是一個字符串數組。但這東西是不抗壓縮的。於是有其他兩種注入:
標記注入:在控制器函數等上面添加一個叫$inject的屬性,對應一個字符串數組,字符串不用說就是各種服務的名稱。那么angular就會在取toString進行推斷式注入前,先進行標記注入。
function TestCtrl(vm, timeout) { vm.friends = [{name: 'John', age: 25}, {name: 'Mary', age: 28}, {name: "Nasami", age: 30} timeout(function() { vm.friends.push({name: "add", age: 10}) }, 1000) } TestCtrl.$inject = ["$scope", "$timeout"]
內聯注入:我們在使用模塊實例的factory,directive, filter或controller方法時,允許第二個傳參是一個數組,這個數組最后一個元素為函數,其他元素為它所依賴的服務名稱。這樣對框架來說,抽取依賴更方便,但對用戶來說,這傳參也太奇誕了。
angular.module('myModule', [], function($provide) { $provide.factory('notify', ['$window', function(win) { var msgs = []; return function(msg) { msgs.push(msg); if (msgs.length == 3) { win.alert(msgs.join("\n")); msgs = []; } }; }]); });
總之一句,IOC是angular為實現依賴收集被逼采取的一個非常惡心的做法,對比knockout,avalon就優雅多了。