點擊查看AngularJS系列目錄
轉載請註明出處:http://www.cnblogs.com/leosx/javascript
每一個Web應用程序都是有多個對象組合、協做來完成任務的。這些對象須要被實例化,而且鏈接在一塊兒進行工做。在AngularJS應用程序中,這些對象都是由injector 注入器服務自動進行實例化和組裝的。而injector 注入器呢,它能夠建立兩種類型的對象: service 服務和 特殊對象。html
特殊對象是遵照了指定的Angular框架的API的。這些對象能夠是一個Controller,也能夠是一個directive指令,也能夠是filter 過濾器,或者animation動畫。。。而咱們的injector注入器須要知道怎麼樣去建立這些對象。那麼你就要經過註冊一個用於建立對象的方法,去告訴注入器到底這個對象該如何建立。java
咱們最多見的經過註冊去告訴注入器如何建立對象的方法有四種: Value, Factory,Service , Constant(常量).一下子咱們來看一下如何在不一樣的場景下經過不一樣的方式去建立和使用一個服務service.api
注意: 爲了使註冊器injector知道如何去建立和組裝鏈接這些全部的對象,它就得須要一個註冊的清單,食譜(list)。清單中的每一個對象都有一個標示符,還有一個對如何建立這個對象的描述。每個清單元素都是一個Angular的module模塊。一個Angular模塊又能夠擁有一個或者多個的清單,也就是說,一個模塊能夠包含它說依賴的其餘模塊的信息。app
一個Angular應用程序是由啓動模塊開始的,Angular會先建立一個injector 注入器的實例。進而去建立把Angular本身的核心的模塊( 「ng」 module),以及他們所依賴的模塊註冊到這個注入器injector的清單中去。 以後,註冊器injector就能夠根據註冊在它的list清單中的信息知道該如何去建立你要的對象了。。。框架
value清單元素(== 我的仍是習慣叫提供者)ide
假設咱們想要一個很是簡單的叫作 「clientId」的服務,它能夠提供一個遠程應用API的認證信息的字符串。你應該這樣去定義:函數
var myApp = angular.module('myApp', []); myApp.value('clientId', 'a12345654321x');
注意,咱們建立了一個叫作 myApp的模塊,這個模塊定義了一個清單,清單中指定了如何構建clientId服務,例子中是一個字符串。動畫
下面來看看如何使用Angular的數據綁定去使用咱們剛纔定義的clientId服務。this
<html ng-app="myApp"> <body ng-controller="DemoController as demo"> Client ID: {{demo.clientId}} </body> </html> // .......js ..... myApp.controller('DemoController', ['clientId', function DemoController(clientId) { this.clientId = clientId; }]);
是否是很簡單,是否是一看就懂。 *^_^*
在這個例子中,咱們使用了Value清單元素去定義了一個叫作clientId的服務,當咱們在Controller裏面使用到的時候,咱們就直接告訴注入器的清單,咱們須要一個叫作clientId的服務,而後咱們就獲得了這個服務的實例了。固然,在這裏,這個服務僅僅是一個字符串而已。。。。
工廠(Factory)清單元素(一個工廠方法的提供者)
Value清單元素是一個很是簡單的元素,在咱們建立好比服務的時候,這樣是徹底不夠的。因此,如今該咱們其餘強大的清單元素登場了: factory 工廠清單元素。 它有如下能力:
1. 能夠去使用其餘的服務(依賴性)
2. 初始化服務。
3. 延遲/ 懶惰加載
工廠清單元素,使用一個能夠有一個或者多個參數的函數方法(function) 去配置如何建立一個新的服務。它的返回值就是那個方法,它知道該如何去建立你要的那個服務。
特別注意: 在Angular中,全部的服務都是單例的哦!! 也就是說注入器只會去建立一次你調用的那個對象,而後之後你再來建立的時候,它直接給你一個引用就好了。而不會再去給你建立一個服務實例。。。
由於factory 工廠清單元素是一個很是強有力的提供者,因此咱們能夠把咱們以前的使用Value清單元素去建立clientId服務的提供者改爲這樣:
myApp.factory('clientId', function clientIdFactory() { return 'a12345654321x'; });
不過須要注意的是,其實在咱們以前的例子中,咱們的clientId只是一個認證憑據,在那種狀況下,仍是Value提供者比較適合於它。咱們這裏只是爲了演示,才把它使用工廠提供者來實現如下的。
咱們再來討論下,比方說,我能海鮮建立一個服務,也拉過來計算一個用於遠程認證的加密的token。這個Token是由剛纔咱們的Value提供者所提供的值和一個咱們祕密存儲在本地的數據計算出來的:
myApp.factory('apiToken', ['clientId', function apiTokenFactory(clientId) { var encrypt = function(data1, data2) { // NSA-proof encryption algorithm: return (data1 + ':' + data2).toUpperCase(); }; var secret = window.localStorage.getItem('myApp.secret'); var apiToken = encrypt(clientId, secret); return apiToken; }]);
上面的例子中,咱們看到了apiToken服務是如何經過工廠(Factory)提供者定義的,它須要依賴於clientId服務。它的實現是將咱們ClientId服務的數據和存儲在本地的數據進行加密後返回。
建議: 咱們在定義服務的時候,最好仍是使用」factory」後綴,雖然不是必須的,可是這樣很是有助於之後的調試和使用。
服務(Service)清單元素
javascript的開發人員一般使用自定義類型來進行面向對象的開發。咱們來看看下面這個例子:
function UnicornLauncher(apiToken) { this.launchedCount = 0; this.launch = function() { // Make a request to the remote API and include the apiToken ... this.launchedCount++; } }
請注意,咱們的UnicornLauncher 是須要apiToken的,咱們可使用工廠提供者來知足UnicornLauncher對apiToken服務的依賴:
myApp.factory('unicornLauncher', ["apiToken", function(apiToken) { return new UnicornLauncher(apiToken); }]);
是否是感受有點兒彆扭啊! 正確的方法是使用Service 提供者去實現.
服務提供者生產服務的過程和Value提供者,Factory工廠提供者的方式相似,可是它是經過 new 操做符去調用的構造函數。固然,你也能夠傳遞零個或者多個的參數進去,表明你這個服務隊其它服務的依賴。
由於咱們準備給咱們的UnicornLauncher類型一個構造函數了,咱們就能夠把上面例子中使用Factory工廠提供者的方式改寫成這樣:
myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);
是否是簡單多了!
Provider 清單元素
就像簽名所述同樣,Provider清單元素是一個很是核心的提供者類型,全部其它的清單元素(提供者)類型都是在Provider清單元素之上的一個語法糖,也就是作了一些包裝。它是能力最強的清單元素,可是對於大多數的服務來講,它太強大了。
Provider清單元素在語法上是一個實現了 $get 方法的自定義類型。它是一個相似於咱們的Factory工廠清單元素的工廠方法。事實上,若是你定義一個Factory清單元素,一個空的provider類型和 $get方法 會根據高級選項去設置你的Factory函數。
僅僅在當你想暴露一個在你的應用程序啓動以前能夠進行的配置API的時候,你可使用Provider清單元素。通常不用,要用的話,一般用在你須要讓你的兩個應用程序只有些許不一樣的時候,你可使用它來進行應用程序配置。
咱們的unicornLauncher 服務真的是很是的有用的。默認狀況下,咱們的啓動器運行的時候是沒任何的屏蔽的。可是,有的時候,咱們在啓動的時候必須作一些防禦。 這是很是很是有用的。若是咱們在咱們的啓動器運行以前,作一些防禦的話,對你的應用程序是很是有利的,咱們能夠這樣來配置:
myApp.provider('unicornLauncher', function UnicornLauncherProvider() { var useTinfoilShielding = false; this.useTinfoilShielding = function(value) { useTinfoilShielding = !!value; }; this.$get = ["apiToken", function unicornLauncherFactory(apiToken) { // let's assume that the UnicornLauncher constructor was also changed to // accept and use the useTinfoilShielding argument return new UnicornLauncher(apiToken, useTinfoilShielding); }]; });
咱們給咱們的應用程序加上了防禦,這個時候,咱們須要建立一個配置功能模塊,並且必須有一個UnicornLauncherProvider 注入進去才能正常啓動:
myApp.config(["unicornLauncherProvider", function(unicornLauncherProvider) { unicornLauncherProvider.useTinfoilShielding(true); }]);
請注意, unicorn 被注入到config函數裏了。它的注入器是經過提供者注入器(provider injector)注入的,而不是普通的注入器注入的。(接下來這句話不知道怎麼翻譯,上原文) in that it instantiates and wires (injects) all provider instances only.
在應用程序啓動期間,在Angular建立全部的服務完畢以前,它會實例化和配置全部的提供者(providers)。咱們把這個配置階段叫作應用程序的生命週期。在這個期間,服務是不可和使用的,由於他們尚未被建立出來!
配置階段結束後,則再也不容許與provider做用(不容許做用,個人理解是不能再修改)了,而後建立服務的進程開始了.咱們把這個階段叫作應用程序生命週期的運行階段。
咱們已經知道了如何將Angular的生命週期分紅配置階段和運行階段。還有你能夠經過config函數給你的應用程序進行配置.甚至若是沒有服務是空的的話,config 配置函數運行在配置階段,而且你是不能夠經過Value清單元素去建立一個值對象的。
在配置階段咱們給unicornLauncher配置了名字。咱們能夠在應用程序的運行期間,經過Controller去使用它。咱們要定義一個constant 能夠這樣作:
myApp.constant('planetName', 'Greasy Giant');
咱們能夠配置unicornLauncherProvider爲這樣:
myApp.config(['unicornLauncherProvider', 'planetName', function(unicornLauncherProvider, planetName) { unicornLauncherProvider.useTinfoilShielding(true); unicornLauncherProvider.stampText(planetName); }]);
咱們可使用常量清單元素Constant建立了有效的常量,也能夠在運行時期使用Value值清單元素去配置,咱們能夠再咱們的Controller和模板(template)中這樣使用:
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>
特殊目的的對象
咱們提到過,咱們也會須要給咱們不一樣的服務建立一些特殊目的的對象。這些對象做爲一種插件來擴展咱們的框架,那麼這樣的對象就須要實現一些Angular指定的接口。這些接口可使controller,directive,Filter和Animation.
下面咱們來介紹若是使用injector去建立這些特殊對象(除了Controller對象之外)。而後在後臺使用Factory工廠清單元素進行綁定。來看看咱們是如何經過剛剛咱們定義的plaetName常量使用Angular的指令去建立一個很是簡單的組件的。
咱們已經知道了,咱們能夠經過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工廠清單元素,你還能夠定義AngularJS的filter(過濾器)和animation(動畫),可是若是是想建立Controller的話,就有點特別了。你建立一個自定義類型的Controller,你能夠再它的聲明中指定它的構造函數中所依賴的對象類型。這個構造函數會被註冊到模塊中去。讓咱們來看看一個咱們很早以前的例子:
myApp.controller('DemoController', ['clientId', function DemoController(clientId) { this.clientId = clientId; }]);
在應用程序當中,每次咱們須要實例化一個DemoController(在咱們的例子中,只實例化了一次)的時候,都會經過它的構造函數去實例化一個DemoController 對象。 因此它不像是服務(service)同樣,是單例模式的。在它的構造函數中能夠調用服務,咱們的這個例子調用的是dlientID服務。
總 結:
1. 注入器(injector)使用清單中的元素來建立兩種對象:服務科特殊對象。
2. 在咱們的清單中,一共有五種類型的清單元素: Value, Factory, Service, Provider, Constant。
3. Factory和Service清單元素是最經常使用的。他們之間的惟一區別是,Service清單元素更加適合自定義類型的對象的建立,而Factory清單元素可使用JavaScript的基元和功能。
4. Provider清單元素是一個很是核心的清單元素類型,並且全部其餘的清單元素類型都是基於它的語法糖而已。
5. Provider是最複雜的清單元素類型。除非你確實須要構建一個可重用的,一個全局通用的代碼的時候,不然你是不須要它的。
6. 全部的特殊對象的定義中,只有Controller的定義是經過Factory清單元素進行配置的。
特性/清單元素類型 | Factory | Service | Value | Constant | Provider |
是否能夠有依賴項 | yes | yes | no | no | yes |
支持使用類型注入 | no | yes | yes* | yes* | no |
對象在配置(config)階段有效 | no | no | no | yes | yes** |
能夠建立函數(function) | yes | yes | yes | yes | yes |
能夠建立基元 | yes | no | yes | yes | yes |
* 表明須要你使用new操做符去直接初始化
** 表明在config 配置階段是無效的,可是Provider實例是有效的(看上面的unicornLauncherProvider例子)