標籤:func for lan debug deb inject 局部變數 led argument
模組是提供一些特殊服務的功能塊。比方本地化模組負責文字本地化,驗證模組負責資料驗證。一般來說,服務在模組內部,當我們須要某個服務的時候,是先把模組執行個體化。然後再調用模組的方法。
但Angular模組和我們通常理解的模組不一樣。Angular模組僅僅保留服務的聲明,服務的執行個體化是由服務注入器完畢的,執行個體化之後服務就留在了服務注入器中,和模組沒有關係了,這就是為什麼我們使用的服務全部來自注入器的原因。
每調用一次angular.boostrap()方法會建立一個新的Angular應用和一個新的服務注入器,因此。每一個應用都相應一個服務注入器。彼此互不衝突。
在angular中,模組能夠是對象、方法(假設是數組,數組的最後一個元素必須是方法。前面的元素都是方法按順序排列的參數名稱)。後面講的模組屬性和方法。都屬於通過angular.module()定義的模組對象。假設模組是方法。是不須要經過angular.module()定義的,僅僅需寫入依賴數組(就是說依賴數組的元素能夠是方法)。模組在載入依賴關係的時候直接運行了。
注意:通過angular.module()方法定義的模組是唯一的。假設反覆定義就會覆蓋前面的定義。
angular模組
angular模組通過angular.module(name,requires, configFn)方法產生:
方法configFn並非在運行angular.module()的時候馬上運行,而是當這個模組被第一次使用時,由注入器調用運行。
同一時候,查看方法configFn中的this就會發現,這個this在瀏覽器中指向的是window。而不是module。並且,方法configFn僅僅會運行一次,因此同一個angular模組不會反覆配置。
參數requires中的字串表示依賴的模組名稱。假設不是字串。則必須是方法(或數組格式的方法),那麼。這種方法就代表了一個模組。
同名模組
已經初始化的angular模組儲存在一個叫modules的緩衝對象中,key是模組名,value是模組對象。所以。定義一個同名的模組,等於覆蓋之前的模組。
服務注入
前面已經講了,angular模組僅僅保留服務的定義,如今我們再來理解服務是怎樣加入注入器的。
在瞭解服務注入器前。還須要瞭解還有一個概念,就是服務提供者。在Angular中稱為Provider,差點兒全部的服務(除了$injector)都是由服務提供者供應。不管是服務還是服務提供者。他們在Angular中都是唯一的,服務和服務提供者是一個一對一的關係。也正是由於這個關係,我們在使用模組service()、value()等方法時,感覺我們定義的好像僅僅是服務,但事實上Angular背後為這些服務都建立了一一相應的服務提供者。注入器的機制就是當我們須要某個服務的時候,首先依據服務名找到相應的服務提供者。然後由服務提供者建立相應的服務並返回。
所以這就是整個過程:
- 模組定義服務、服務提供者。
- 注入器依據模組依賴關係載入模組,執行個體化全部服務提供者;
- 應用須要服務,注入器依據服務名尋找服務提供者。服務提供者執行個體化服務。
以上僅僅是理論。如今從代碼層面來看Angular是怎樣實現的。
每一個angular模組內建有三個數組。invokeQueue儲存怎樣注入服務提供者和值的資訊;configBlocks儲存模組的配置資訊。runBlocks儲存這個模組的運行資訊。
模組被使用的時候,注入器依據invokeQueue中的資訊。執行個體化服務提供者;依據configBlocks中的資訊對服務提供者做一些額外的處理;依據runBlocks中提供的資訊,調用前面的服務提供者提供的服務運行模組須要完畢的工作。
angular模組提供了非常多方法來填充這三個數組,比方config()、run()等。三個數組的元素也是數組。詳細元素格式參考後面的說明。
調用隊列 – invokeQueue
數組元素為[‘provider’, ‘method’, arguments]。
舉例– 加入一個Controller:
angular.module(‘ngAppDemo‘,[]).controller(‘ngAppDemoController‘,function($scope) { $scope.a= 1; $scope.b = 2;});
這段代碼等於:
invokeQueue.push([‘$controllerProvider‘,‘register‘, [‘ngAppDemoController‘, function(){}]]);
注入器依據這個資訊,就會調用$controllerProvider的register方法注冊一個ngAppDemoController。
配置隊列 – configBlocks
元素格式為[‘$injector‘, ‘invoke‘, arguments]。
運行隊列 – runBlocks
元素要求是方法,或者是數組。數組最後一個元素是方法。
angular模組執行個體屬性和方法屬性requires
表示模組的依賴數組。即angular.module方法的requires參數。
name
模組的名字。
_invokeQueue、_configBlocks、_runBlocks
分別相應invokeQueue、configBlocks、runBlocks。
方法
模組的下面方法最後全部會返回模組執行個體本身,形成運行鏈。
animation()
調用這種方法表示這個模組將在$animateProvider中注冊一個動畫服務。
原理:在invokeQueue尾部插入[‘$animateProvider‘, ‘register‘, arguments]。
config()
調用這種方法,表示給這個模組追加一個配置方法。
原理:在configBlocks尾部插入[‘$injector‘,‘invoke‘, arguments]。
constant()
調用這種方法表示這個模組將給預設的$provider注冊一個常量。
原理:在invokeQueue首部插入[‘$provide‘, ‘constant‘, arguments]。
controller()
調用這種方法表示模組將在$controllerProvider中注冊一個控制器。
原理:在invokeQueue尾部插入[‘$controllerProvider‘, ‘register‘, arguments]。
directive()
調用這種方法表示這個模組將在$compileProvider中注冊一個指令。
原理:在invokeQueue尾部插入[‘$compileProvider‘, ‘directive‘, arguments]。
factory()
調用這種方法表示這個模組中將產生一個服務工廠(隱式建立一個了服務提供者)。
原理:在invokeQueue尾部插入[‘$provide‘, ‘factory‘, arguments]。
filter()
調用這種方法表示這個模組將在$filterProvider中注冊一個過濾器。
原理:在invokeQueue尾部插入[‘$filterProvider‘, ‘register‘, arguments]。
provider()
調用這種方法表示這個模組將加入一個服務提供者。
原理:在invokeQueue尾部插入[‘$provide‘, ‘provider‘, arguments]。
run(block)
調用這種方法表示這個模組將運行某個功能塊。block能夠是方法,也能夠是數組。
原理:在invokeQueue尾部插入block。
service()
調用這種方法表示這個模組將注冊一個服務(隱式建立了一個服務提供者)。
原理:在invokeQueue尾部插入[‘$provide‘, ‘service‘, arguments]。
value()
調用這種方法表示這個模組將注冊一個變數(隱式建立了一個服務提供者)。
原理:在invokeQueue尾部插入[‘$provide‘, ‘value‘, arguments]。
服務注入器(Service Injector) & 服務提供者(Service Provider)
在Angular中,服務可能是對象、方法、或者一個常量值。服務由服務提供者建立。而服務提供者由注入器統一管理。當我們須要某個服務的時候。注入器負責依據服務名尋找相應的服務提供者,然後由服務提供者的$get()生產工廠建立服務執行個體。因此。服務提供者必須有一個$get()方法,這種方法就是服務建立單例工廠。
背後原理:注入器中的Providers和Services各自通過一個Map對象儲存在緩衝(分別相應providerCache和instanceCache)中,僅僅只是Providers的key是serviceName + “Provider”,而Services的key是serviceName。
服務提供者-Provider
Provider即服務提供者,必須有一個$get()方法。$get()的傳回值是Provider在注入器中實際的服務。
注入器
建立注入器的方法僅僅在bootstrap()方法中被調用過,也就是說,每一個angular應用相應一個注入器。
注入過程
注入器由angular.injector(modulesToLoad, isStrictDi)方法建立。在angular中事實上為createInjector方法。參數modulesToLoad是數組,元素格式為下面之中的一個:
- ‘module’,模組的名稱。
- [‘service1’, ‘service2’, fn]。
- fn,方法的傳回值必須仍然是方法。
方法angular.injector()的運行過程:
1. 遍曆modulesToLoad。依據moduleName尋找或產生相應的模組。
2. 調用模組invokeQueue中的全部方法。這一步的目的是建立必需的服務。
3. 調用模組configBlocks中的全部方法,目的是用已有的服務配置這個模組。
4. 調用注入器的invoke()方法運行模組runBlocks的全部方法。
5. 返回服務注入器執行個體。
不是全部模組都是對象,假設模組本身是方法或者是數組(最後一個元素必須是方法)。則運行這種方法、或數組的最後一個方法,相當於直接進入了第四步。
注入器與bootstrap的關係
建立注入器的方法在angular.js中僅僅在bootstrap()方法中被調用過。也就是說,每一個angular應用相應一個注入器。
注入器方法
在providerCache中和instanceCache中分別內建有一個$injector對象,分別負責給模組注入服務提供者和為方法注入服務。
一般我們僅僅談論instanceCache中的$injector對象。由於providerCache和它的$injector是私人的,僅僅在Angular內部代碼使用。
比方,運行模組調用隊列、配置隊列中的方法時注入的是服務提供者,而當調用運行隊列中的方法時,注入的是服務。
$injector.invoke(fn, self, locals, serviceName)
運行方法fn。
locals是可選參數,是對象,表示局部變數。
self是fn中的this。
最後一個參數serviceName是可選參數,表示在哪個服務中調用了fn方法,用於錯誤資訊顯示,沒有處理邏輯。
$injector.instantiate(Type, locals, serviceName)
l 假設參數Type為方法,依據Type的prototype建立一個執行個體(通過Object.create方法建立),假設Type是數組。使用最後一個元素的prototype。
l 參數Locals是當Type方法的參數出如今locals對象中的時候。取locals[arg]的值又一次作為Type的參數。假設locals中沒有。則等價於調用get(arg,serviceName)擷取service作為新的參數。
執行個體化過程能夠簡單概括為
Type.apply(Object.create(Type.prototype),locals[argName]|| get(argName, serviceName))。
注意:執行個體化出來的不一定是對象。也可能是方法。
最後一個參數serviceName是可選參數,表示在哪個服務中執行個體化了Type,用於錯誤資訊顯示,沒有處理邏輯。
$injector.get(name, caller)
從注入器中擷取一個服務執行個體。
參數name是服務的名稱。參數caller也是字串,表示調用這個服務的是哪個方法,用於錯誤資訊提示。沒有處理邏輯。
$injector.annotate(fn[,strictDi][,name] )
返回數組。數組的元素是fn方法須要注入的依賴服務。
在strict 模式下,方法的依賴注入必須使用顯示的註解加入,也就是說通過fn.$injector能夠擷取這種方法的依賴注入。
參數name是可選的,用於錯誤顯示,沒有處理邏輯。
方法annotate()也能夠接受數組,數組的最後一個參數一定是fn,前面的元素則是依賴。
$injector.has(name)
檢查該注入器中是否存在指定的服務。
Provider方法
注入器的providerCache中內建有一個$provider對象,這是注入器的預設服務提供者,$provider有六個固定的方法。這幾個方法的作用主要是為注入器加入其他服務提供者。
注意:
- 下面全部方法的name參數不須要以“Provider”結尾,由於provider()方法會預設把這個尾碼加上。
- 下面不論什麼一個方法不做同名推斷,因此,假設出現同名,後者將覆蓋前者。
$provider.provide(name, provider)
參數provider能夠是方法或數組,也能夠是對象。
l 假設是方法,則是provider的建構函式。
調用注入器的instantiate()方法,產生一個provider執行個體。並以name為key儲存在注入器的providerCache中。
l 假設是數組。最後一個必須是provider的建構函式。前面的就是建構函式的參數名。之後的原理和provider是方法的情形同樣。
l 假設是對象,說明這個provider已經被執行個體化了,僅僅需有$get()方法就可以。
$provider.factory(name, factoryFn, enforce)
使用$provider.provide()一般須要定義一個Provider類,假設不想定義Provider類,而是直接定義服務工廠。就能夠使用這種方法。
背後原理:首先產生一個匿名對象,這個對象的$get屬性就是factoryFn(enforce為false的情況下),然後把這個匿名對象作為$provider.provide()方法的第二個參數。所以。factoryFn事實上依舊是綁定在一個provider上的。
$provider.service(name, constructor)
調用injector.instantiate()方法,利用參數constructor產生service執行個體,參數name是這個service的名稱。
眾所周知,service由provider提供,那這種方法是怎麼回事?原理:Angular首先依據constructor產生一個factoryFn,然後調用$provider.factory(name, factoryFn)。
所以事實上還是產生了一個provider。
舉例:
$provider.service(‘filter‘, constructor)
等於建立了一個filter服務執行個體。並且在providerCache中儲存了一個名稱為“filterProvider”的服務提供者。
$provider.value(name, value)
這種方法實際調用injector.factory(name,valueFn(value), false)方法實現。所以事實上等於建立一個僅僅提供值的服務提供者。
$provider.constant(name, value)
這種方法直接在providerCache中加入一個屬性實現。
$provider.decorate(serviceName, decorFn)
改動舊的服務,改為運行decorFn方法,並把servcieName原來的服務作為一個參數。參數名為$delegate。等價於一個靜態代理。
背後原理:首先依據seviceName找到Provider,然後改動provider的$get屬性。
服務執行個體化
全部服務都由Provider管理,除了常量值,其他一般都是還沒有建立出來的。
瞭解了注入器的全部方法。也應該能夠看出,僅僅有兩個方法:get()和invoke()真實的調用了服務。
事實上,服務的執行個體化實際是在注入器的get()方法中完畢的。而invoke()僅僅是在須要的時候調用了get()。
angular內建模組ngLocale - 本地化模組
angular.module(‘ngLocale‘,[]).provider(‘$locale‘, $LocaleProvider);
結果是invokeQueue.push([‘$provide‘, ‘provider‘,[‘$locale‘, $LocaleProvider]]);
ng
angular.module(‘ng‘,[‘ngLocale‘]).config([‘$provide‘, function(){}]);
結果是configBlocks.push([‘$injector‘, ‘invoke‘,[‘$provide‘, function(){}]])。
三個固定模組
每一個使用bootstrap(element, modules, config)產生的應用,注入器中有三個固定的模組:
- 第一個模組是"ng"。
- 第二個模組是[‘$provider’,fn],它的作用是把根項目element作為變數儲存在$provider中。
- 第三個模組是[‘$compileProvider’,fn],它的作用是依據config.debugInfoEnabled調用 $conpileProvider.debugInfoEnabled(true)。
AngularJS模組具體解釋