AngularJS学习笔记之依赖注入

     最近在看AngularJS权威指南,因为各类各样的缘由(主要是由于我没有money,好讨厌的有木有......),因而我选择了网上下载电子版的(由于它不要钱,哈哈...),字体也蛮清晰的,整体效果还不错。可是,当我看到左上角的总页码的时候,479页....479....479....俺的当心脏被击穿了二分之一有木有啊,上半身都石化了有木有啊,那种特别想学可是看到页码又不想学的纠结的心情比和女友吵架了还复杂有木有啊,我日常看的电子书百位数都不大于3的好伐! 哎,原谅我吧,我应该多看几本新华字典习惯习惯的...前端

     不过幸亏在看电子书以前,我已经稍微有点基础了,以前看着视频学习了一些,从双向数据绑定到服务,而后到指令系统,都多多少少有些接触。而且在一次web专选课结课做业当中,经过前端的AngularJS和后台的NodeJS加Mongoose搭建了一个简易学生班级管理系统。由于没有钱,因此只能放在GitHub了,GitHub地址: 学生管理系统,欢迎来fork哈,下面进入正题...node

 

=======================================请叫我华丽的分割线=======================================git

     

一个对象一般有三种方式能够得到对其依赖的控制权:
  (1) 在内部建立依赖;
  (2) 经过全局变量进行引用;
  (3) 在须要的地方经过参数进行传递。
     依赖注入是经过第三种方式实现的。其他两种方式会带来各类问题,例如污染全局做用域,使隔离变得异常困难等。依赖注入是一种设计模式,它能够去除对依赖关系的硬编码,从而能够在运行时改变甚至移除依赖关系。angularjs

     在运行时修改依赖关系的能力对测试来说是很是理想的,由于它容许咱们建立一个隔离的环境,从而在测试环境可使用模拟的对象取代生产环境中的真实对象。github

     从功能上看,依赖注入会事先自动查找依赖关系,并将注入目标告知被依赖的资源,这样就能够在目标须要时当即将资源注入进去。web

     在编写依赖于其余对象或库的组件时,咱们须要描述组件之间的依赖关系。在运行期,注入器会建立依赖的实例,并负责将它传递给依赖的消费者。设计模式

1 // 出自Angular文档的优秀示例
2 function SomeClass(greeter) {
3 this.greeter = greeter;
4 }
5 SomeClass.prototype.greetName = function(name) {
6 this.greeter.greet(name);
7 };
8 //注意,示例代码在全局做用域上建立了一个控制器,这并非一个好主意,这里只是为了方便演示。

     SomeClass 可以在运行时访问到内部的 greeter ,但它并不关心如何得到对 greeter 的引用。为了得到对 greeter 实例的引用, SomeClass 的建立者会负责构造其依赖关系并传递进去。
     基于以上缘由,AngularJS使用 $injetor (注入器服务)来管理依赖关系的查询和实例化。事实上, $injetor 负责实例化AngularJS中全部的组件,包括应用的模块、指令和控制器等。
     在运行时,任何模块启动时 $injetor 都会负责实例化,并将其须要的全部依赖传递进去。
     例以下面这段代码。这是一个简单的应用,声明了一个模块和一个控制器:数组

 1 angular.module('myApp', [])
 2 .factory('greeter', function() {
 3 return {
 4 greet: function(msg) {alert(msg);}
 5 }
 6 })
 7 .controller('MyController',
 8 function($scope, greeter) {
 9 $scope.sayHello = function() {
10 greeter.greet("Hello!");
11 };
12 });

    当AngularJS实例化这个模块时,会查找 greeter 并天然而然地把对它的引用传递进去:app

1 <div ng-app="myApp">
2 <div ng-controller="MyController">
3 <button ng-click="sayHello()">Hello</button>
4 </div>
5 </div>

    而在内部,AngularJS的处理过程是下面这样的:函数

1 // 使用注入器加载应用
2 var injector = angular.injector(['ng', 'myApp']);
3 // 经过注入器加载$controller服务
4 var $controller = injector.get('$controller');
5 // 加载控制器并传入一个做用域,同AngularJS在运行时作的同样
6 var scope = injector.get('$rootScope').$new();
7 var MyController = $controller('MyController', {$scope: scope});

    上面的代码中并无说明是如何找到 greeter 的,可是它的确能正常工做,由于 $injector会负责为咱们查找并加载它。
    AngularJS经过 annotate 函数,在实例化时从传入的函数中把参数列表提取出来。在Chrome的开发者工具中输入下面的代码能够查看这个函数:

 > injector.annotate(function($q, greeter) {})
 ["$q", "greeter"]

   在任何一个AngularJS的应用中,都有 $injector 在进行工做,不管咱们知道与否。当编写控制器时,若是没有使用 [] 标记或进行显式的声明, $injector 就会尝试经过参数名推断依赖关系。

 

   推断式注入声明  

   若是没有明确的声明,AngularJS会假定参数名称就是依赖的名称。所以,它会在内部调用函数对象的 toString() 方法,分析并提取出函数参数列表,而后经过 $injector 将这些参数注入进对象实例。注入的过程以下:

   injector.invoke(function($http, greeter) {});

   请注意,这个过程只适用于未通过压缩和混淆的代码,由于AngularJS须要原始未经压缩的参数列表来进行解析。有了这个根据参数名称进行推断的过程,参数顺序就没有什么重要的意义了,由于AngularJS会帮助咱们把属性以正确的顺序注入进去。

 

   式注入声明 

   AngularJS提供了显式的方法来明肯定义一个函数在被调用时须要用到的依赖关系。经过这种方法声明依赖,即便在源代码被压缩、参数名称发生改变的状况下依然可以正常工做。能够经过$inject 属性来实现显式注入声明的功能。函数对象的 $inject 属性是一个数组,数组元素的类型是字符串,它们的值就是须要被注入的服务的名称。
   下面是示例代码:

 1 var aControllerFactory =
 2 function aController($scope, greeter) {
 3 console.log("LOADED controller", greeter);
 4 // ……控制器
 5 };
 6 aControllerFactory.$inject = ['$scope', 'greeter']; // Greeter服务
 7 console.log("greeter service");
 8 }
 9 // 咱们应用的控制器
10 angular.module('myApp', [])
11 .controller('MyController', aControllerFactory)
12 .factory('greeter', greeterService);
13 // 获取注入器并建立一个新的做用域
14 var injector = angular.injector(['ng', 'myApp']),
15 controller = injector.get('$controller'),
16 rootScope = injector.get('$rootScope'),
17 newScope = rootScope.$new();
18 // 调用控制器
19 controller('MyController', {$scope: newScope});

   对于这种声明方式来说,参数顺序是很是重要的,由于 $inject 数组元素的顺序必须和注入参数的顺序一一对应。这种声明方式能够在压缩后的代码中运行,由于声明的相关信息已经和函数自己绑定在一块儿了。

 

   行内注入声明

   AngularJS提供的注入声明的最后一种方式,是能够随时使用的行内注入声明。这种方式实际上是一个语法糖,它同前面提到的经过 $inject 属性进行注入声明的原理是彻底同样的,但容许咱们在函数定义时从行内将参数传入。此外,它能够避免在定义过程当中使用临时变量。
   在定义一个AngularJS的对象时,行内声明的方式容许咱们直接传入一个参数数组而不是一个函数。数组的元素是字符串,它们表明的是能够被注入到对象中的依赖的名字,最后一个参数就是依赖注入的目标函数对象自己。
   示例以下:

1 angular.module('myApp')
2 .controller('MyController', ['$scope', 'greeter', function($scope, greeter) {
3 }]);

   因为须要处理的是一个字符串组成的列表,行内注入声明也能够在压缩后的代码中正常运行。一般经过括号和声明数组的 [] 符号来使用它。