点击查看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例子)