原版地址:http://code.angularjs.org/1.0.2/docs/guide/dijavascript
1、Dependency Injection(依赖注入)html
依赖注入(DI)是一个软件设计模式,处理代码如何获得它所依赖的资源。java
关于DI更深层次的讨论,能够参观Dependency Injection(http://en.wikipedia.org/wiki/Dependency_injection),Inversion of Control(http://martinfowler.com/articles/injection.html),也能够参观软件设计模式的书。angularjs
1. DI in a nutshell(简说DI)shell
object或者function,只可以经过如下三种方式获取他们依赖的资源:express
1) 能够经过new运算符建立依赖的资源。bootstrap
2) 能够经过全局变量查找依赖的资源。设计模式
3) 能够经过参数传入依赖的资源。api
一、2两种方式,并非最佳的,由于它们对依赖关系进行hard code,这使得修改依赖关系时,不是不可能,但会变得比较复杂。这对于测试来讲尤为是个问题,一般在独立测试时,但愿可以提供模拟的依赖资源。数组
第3种方法相对来讲最可行,由于它去除了从组件(component)中定位依赖的责任。依赖仅仅交给组件就能够了。
function SomeClass(greeter) { this.greeter = greeter } SomeClass.prototype.doSomething = function(name) { this.greeter.greet(name); }
这样是比较合适的,但它将获取依赖资源的责任交给了负责构建SomeClass的代码那里。
为了管理建立依赖的责任,每个angular应用都有一个injector(http://code.angularjs.org/1.0.2/docs/api/angular.injector)。injector是一个服务定位器,负责定位并建立依赖的资源。
请求依赖,解决了hard code的问题,但它意味着injector须要贯穿整个应用。传递injector,会破坏Law of Demeter(http://baike.baidu.com/view/823220.htm)。为了纠正这个问题,咱们将依赖查找的责任转给injector。
上面说了那么多,看看下面经我修改过的例子,合并了原文的两个例子,分别在angular内、外使用inject:
<!DOCTYPE HTML> <html lang="zh-cn" ng-app="MainApp"> <head> <meta charset="UTF-8"> <title>injector</title> </head> <body> <div ng-controller="MyController"> <button ng-click="sayHello()">Say Hello</button> </div> <script src="../angular-1.0.1.js" type="text/javascript"></script> <script type="text/javascript"> //建立OtherModule这个module,至关于外部的module var otherModule = angular.module("OtherModule", []); //教injector如何建立"greeter" //注意,greeter自己须要依赖$window otherModule.factory("greeter", function ($window) { //这里是一个工厂方法,负责建立greet服务 return { greet:function (text) { $window.alert(text); } }; }); //下面展现在非当前module中,经过injector调用greet方法: //从module中建立新的injector //这个步骤一般由angular启动时自动完成。 //必须引入'ng',angular的东东 //故意颠倒顺序,暂时证明这玩意的顺序是无所谓的。。 var injector = angular.injector(['OtherModule','ng']); //请求greeter这个依赖。 var g = injector.get("greeter"); //直接调用它~ g.greet("Hi~My Little Dada~"); //这里是当前的主app,须要依赖OtherModule var mainApp = angular.module("MainApp", ["OtherModule"]); //留意Controller的定义函数的参数,在这里直接注入$scope、greeter。 // greeter服务是在OtherModule中的 mainApp.controller("MyController",function MyController($scope,greeter) { $scope.sayHello = function() { greeter.greet("Hello Kitty~~"); }; } ); //ng-controller已经在背后默默地作了这个事情 //injector.instantiate(MyController); </script> </body> </html>
注意,由于有ng-controller,初始化了MyController,它能够知足MyController的全部依赖须要,让MyController无须知道injector的存在。这是一个最好的结果。应用代码简单地请求它所须要的依赖而不须要处理injector。这样设置,不会打破Law of Demeter。
2、Dependency Annotation(依赖注释,说明依赖的方式)
injector如何知道什么服务须要被注入呢?
应用开发者须要提供被injector用做解决依赖关系的注释信息。全部angular已有的API函数,都引用了injector,每个文档中说起的API都是这样。下面是用服务名称信息注释咱们的代码的三个等同的方法。
1. Inferring Dependencies(隐含依赖)
这是获取依赖资源的最简单的方式,但须要假定function的参数名称与依赖资源的名称一致。
function MyController($scope, greeter) { ... }
虽然这样作很简单,但这方法在javascript混淆压缩后就行不通了,由于参数名称会被改变。这让这个方式只能对pretotyping(产品可用性原型模拟测试法,http://www.pretotyping.org/,http://tech.qq.com/a/20120217/000320.htm)和demo应用有做用。
2. $inject Annotation($inject注释)
为了容许脚本压缩器重命名函数的方法后,仍然可以注入正确的服务,函数必须经过$inject属性来注释依赖。$inject属性是一个须要注入的服务的名称的数组。
var MyController = function(renamed$scope, renamedGreeter) { ... } //这里依赖的东东,若是不在当前的module中,它仍是不认识的。
//须要在当前module中先依赖对应的module。跟以前的例子差很少。但我不知道这是否是正确的方法。 MyController.$inject = ['$scope', 'greeter'];
这个注释方法,对于controller声明来讲是有用的,由于它与函数一块儿指定注释信息。
3. inline Annotation(行内注释)
有时候,不方便使用$inject注释的方式,例如注释directive的时候。
例如:
someModule.factory('greeter', function($window) { ...; });
var greeterFactory = function(renamed$window) { ...; }; greeterFactory.$inject = ['$window']; someModule.factory('greeter', greeterFactory);
因为这样(代码膨胀),angular还提供了第三种注释风格:
someModule.factory('greeter', ['$window', function(renamed$window) { ...; }]);
3、Where can I user DI?
DI遍布整个angular。它一般使用在controller和factory方法中。
1. DI in controllers
controller是负责(描述)应用行为的类。建议的controller声明方法是:
var MyController = function(dep1, dep2) { ... } MyController.$inject = ['dep1', 'dep2']; MyController.prototype.aMethod = function() { ... }
factory方法是负责建立大多数angular对象。例如directive、service、filter。factory方法注册在module中,建议的factory声明方法是:
angualar.module('myModule', []). config(['depProvider', function(depProvider){ ... }]). factory('serviceId', ['depService', function(depService) { ... }]). directive('directiveName', ['depService', function(depService) { ... }]). filter('filterName', ['depService', function(depService) { ... }]). run(['depService', function(depService) { ... }]);