在AngularJS中,app中的大多數對象經過injector服務初始化和鏈接在一塊兒。javascript
Injector建立兩種類型的對象,service對象和特別對象。php
Service對象由開發者自定義api。html
特別對象則遵守AngularJS框架特定的api,這些對象包括:controller, directive, filter or animation。java
最詳細最全面的是Provider,其餘四種(Value, Factory, Service and Constant)只是在Provider「」之上包裝了一下而已。 angularjs
爲了使injector知道怎麼樣建立和連接這些對象,你須要recipe的註冊表。每個recipe都有一個對象的id和怎樣建立對象的說明。express
當AngularJS使用給定的app module啓動app時,angularjs會建立一個新的injector實例,injector會依次把核心module的「配方」加入「配方」註冊表,而且建立app module和他的依賴。當須要爲app建立對象時,就會去查找「配方」註冊表。設計模式
代碼中可能多個地方須要訪問一個共享的字符串,咱們先用Value「配方」來實現這種情境。api
咱們來建立一個很是簡單的service,該service提供一個用於遠程api鑑權的id字符串,能夠這麼寫:瀏覽器
<!DOCTYPE html> <html ng-app="myApp"> <head> <meta charset="uft-8"/> <title></title> </head> <script src="script/angular.min.js"></script> <body ng-controller="DemoController as demo"> Client ID: {{demo.clientId}} </body> <script> var myApp = angular.module('myApp', []); myApp.value('clientId', 'a12345654321x'); myApp.controller('DemoController', ['clientId', function DemoController(clientId) { this.clientId = clientId; }]); </script> </html>
咱們建立了一個名字叫myApp的module,而且指定該module的定義包含一個構建clientId服務的recipe,這個例子中service僅僅是一個字符串。緩存
這個例子中咱們使用value「配方」定義了一個值供DemoController須要一個clientId的service時調用。
Value「配方」雖然簡單,可是缺乏咱們建立service時須要的不少重要的功能,Factory「配方」就強大不少,有如下功能:
Factory「配方」可使用0個或者多個參數建立service,這些參數能夠是依賴的其餘service。函數的返回值是一個經過「配方」建立的service實例。
在angularjs中全部的service都是單例的,這意味着injector爲了建立對象只使用每一個一次,而後injector把service的引用緩存起來,以便未來能夠調用。
示例:
<!DOCTYPE html> <html ng-app="myApp"> <head> <meta charset="uft-8"/> <title></title> </head> <script src="script/angular.min.js"></script> <body ng-controller="DemoController as demo"> Client ID: {{demo.clientId}} </body> <script> var myApp = angular.module('myApp', []); //myApp.value('clientId', 'a12345654321x'); myApp.factory("clientId",function(){ return "a12345654321x"; }); myApp.controller('DemoController', ['clientId', function DemoController(clientId) { this.clientId = clientId; }]); </script> </html>
若是僅僅是返回一個token這麼簡單,那麼value「配方」則更合適,由於寫起來簡單也容易明白。
可是咱們但願建立一個複雜點的service來計算token作遠程api的身份驗證,這個token叫作apiToken,經過clientId的值和瀏覽器本地存儲中的secret值計算得出。
以上代碼能夠看到如何依賴clientId服務經過Factory「配方」建立apiToken服務。
factory的函數名最好使用<serviceId>Factory
形式命名,例如apiTokenFactory
,固然函數名是能夠不要的,可是爲了調試方便仍是最好寫上。
同Value」配方「同樣,Factory」配方「能夠建立任意類型的服務,不管是原始類型,對象,函數仍是自定義的類型。
javascript開發者常常喜歡寫面向對象的自定義類型,如今讓咱們經過unicornLauncher服務把一個unicorn發射到太空,unicornLauncher是一個自定義類型的實例。
function UnicornLauncher(apiToken) { this.launchedCount = 0; this.launch = function() { // 使用apiToken訪問遠程api ... this.launchedCount++; } }
如今咱們準備發射獨角獸,注意到UnicornLauncher依賴於apiToken,咱們經過Factory」配方「來知足這個依賴:
myApp.factory('unicornLauncher', ["apiToken", function(apiToken) { return new UnicornLauncher(apiToken); }]);
然而這個例子使用service「配方」更爲合適
Service」配方「能夠像Value」配方「和Factory「配方」同樣生產service,可是它能夠經過new操做符調用對象的構造函數。構造函數的參數能夠經過Service「配方中」的依賴項加入。
Service「配方」的設計模式叫作構造器注入(constructor injection)。
既然我已經有了UnicornLauncher多多構造器,咱們能夠把Factory」配方」替換爲Service「配方」。
myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);
Provider「配方」是核心「配方」,其餘全部的「配方」僅僅是在它裏面加了點糖。它是最多能力最詳細的「配方」,可是對於大多數service來講,不是全部的能力都有用。
Provider「配方」定義了一個實現了$get
方法的自定義類型。$get
方法是一個Factory函數,很像Factory「配方」定義的Factory函數同樣。事實上,若是你定義了一個Factory「配方」,框架會建立一個空的Provider類型,其中的$get
方法就會指向你定義的Factory方法。
在app啓動時,咱們須要一些配合時,這個時候纔會須要Provider披露的API。一般是一些能夠重用的service。
默認狀況下,發射器發射unicorn到太空是沒有任何防禦的,可是有些行星上大氣層太厚了,在unicorn開始它的太空旅行以前,咱們須要爲它包裹tinfoil,不然她沒經過大氣層時會摩擦燃燒。配置代碼以下:
myApp.provider('unicornLauncher', function UnicornLauncherProvider() { var useTinfoilShielding = false; this.useTinfoilShielding = function(value) { useTinfoilShielding = !!value; }; this.$get = ["apiToken", function unicornLauncherFactory(apiToken) { // 咱們假設UnicornLauncher構造函數已更改,能夠接收useTinfoilShielding參數 return new UnicornLauncher(apiToken, useTinfoilShielding); }]; });
在程序啓動時,config函數中調用unicornLauncherProvider的方式以下:
myApp.config(["unicornLauncherProvider", function(unicornLauncherProvider) { unicornLauncherProvider.useTinfoilShielding(true); }]);
unicornLauncherProvider是被注入到config方法中的,這裏的injector和常規的實例injector不一樣,它只負責provider的初始化和鏈接。
在app啓動之時,在建立service以前,它會配置和實例化全部的provider,咱們把這個叫作app生命週期的配置階段,在這個階段,service還不可以使用,由於他們還未建立。
一旦配置階段完成,那麼全部的provider變爲不可用,建立service的進程開始啓動,這個階段被叫作app生命週期的運行階段。
咱們剛剛瞭解瞭如何區分app生命週期的配置階段和運行階段,還有怎麼樣經過config函數配置app。由於配置函數在配置階段執行,這時service都是不能夠用的,它甚至沒法訪問經過Value「配方」建立的簡單的值對象。
有一些簡單的值,好比url的前綴,沒有任何依賴和配置,經常須要配置階段和運行階段均可以方便的訪問,
Constant「配方」的做用就在這了。
假設咱們的unicornLauncher服務須要使用它的所在行星的名字來標記,行星的名字能夠在配置階段提供。行星的名字對於每一個app是特定的,運行階段會在各類各樣的controller中使用。咱們能夠這樣這樣定義行星的名字做爲一個常量:
myApp.constant('planetName', 'Greasy Giant');
配置unicornLauncherProvider以下:
myApp.config(['unicornLauncherProvider', 'planetName', function(unicornLauncherProvider, planetName) { unicornLauncherProvider.useTinfoilShielding(true); unicornLauncherProvider.stampText(planetName); }]);
Constant「配方」和Value「配方」同樣在配置階段是可用的,可用用於controller和模板:
myApp.controller('DemoController', ["clientId", "planetName", function DemoController(clientId, planetName) { this.clientId = clientId; this.planetName = planetName; }]);
<html ng-app="myApp"> <body ng-controller="DemoController as demo"> Client ID: {{demo.clientId}} <br> Planet Name: {{demo.planetName}} </body> </html>
以前提到有些特殊用途的對象和service是不一樣的,這些對象做爲插件來擴展angularjs框架,須要實現angularjs指定的接口,這些接口是Controller, Directive, Filter 和 Animation。
這些特殊對象除controller外,其他的在底層實際上是由Factory「配方」建立的。
在下面的例子中,咱們演示如何經過Directive的api建立一個簡單的組件,這個組件依賴於咱們以前定義的planetName常量,並顯示這個常量:「Planet Name: Greasy Giant」
因爲Directive是經過Factory「配方」註冊的,咱們採用Factory相同的語法:
myApp.directive('myPlanet', ['planetName', function myPlanetDirectiveFactory(planetName) { // directive definition object return { restrict: 'E', scope: {}, link: function($scope, $element) { $element.text('Planet: ' + planetName); } } }]);
而後使用這個組件:
<html ng-app="myApp"> <body> <my-planet></my-planet> </body> </html>
使用Factory「配方」也能夠定義filter和animation,可是controller有點特殊。你能建立controller做爲一個自定義類型,它的依賴能夠從它的構造函數中傳入,這個構造器又被module用來註冊controller。咱們來看下我以前寫的DemoController:
myApp.controller('DemoController', ['clientId', function DemoController(clientId) { this.clientId = clientId; }]);
每次app須要DemoController實例的時候,就好調用它的構造器進行初始化。所以和service不同,controller不是單例的。構造器能夠注入其餘service。
讓咱們來總結一下:
Features / Recipe type | Factory | Service | Value | Constant | Provider |
---|---|---|---|---|---|
可否依賴與其餘對象 | yes | yes | no | no | yes |
可否友好的注入到其餘對象 | no | yes | yes* | yes* | no |
建立的對象配置階段是否可用 | no | no | no | yes | yes** |
可否建立函數 | yes | yes | yes | yes | yes |
可否建立原生類型 | yes | no | yes | yes | yes |
* 須要使用new操做符付出預先初始化的代價
** service在配置階段不可用,可是provider對象在配置階段可用