先了解依赖
首先我们要了解什么是依赖;举个例子吧,比如你玩撸啊撸,你看到别人在玩小提莫,你觉得挺好玩,也想玩这个英雄,那么你就要去商店把这个英雄买下来,然后才可以使用这个英雄,拥有这个英雄就是你的依赖;简单的说,依赖就是你达到某个目的的必要条件。
依赖注入是一种软件的设计模式,它允许你移除软件组件之间的硬编码方式,替代的是通过依赖注入制造低耦合的组件不论是在编译阶段还是在运行阶段。
AngularJS有一个内在的依赖注入机制,使用AngularJS,你可以把你的App分成许多个可以重复使用的组件,当需要这些组件的时候,可以通过依赖注入把这些组件注入到你的App中去。
获得对依赖的控制权
通常一个对象有三种方法可以获得对其依赖的控制权
在内部创建依赖:这种方法的弊端就是不方便以后的维护,使隔离变的异常困难。
通过全局变量进行引用:这种方法的弊端就是污染了全局作用域,当代码量达到了一定程度后,容易出现问题。
在需要的地方进行参数传递:这种方法不仅对测试很有用,而且不会污染全局变量,是很好的设计模式。
AngularJS
用的就是这种方法。
AngularJS依赖注入的方法
1. 通过函数的参数进行推断式注入声明
如果没有明确的声明,AngularJS
会假定参数名称就是依赖的名称。因此,它会在内部调用函数对象的toString()
方法,分析并提取出函数的参数列表,然后通过 $injector
将这些参数注入进对象实例。
下面是代码示例:
HTML代码:
html
<div ng-app> <div ng-controller="MyController"> <p ng-cloak>{{clock.time | date: "yyyy-MM-dd hh:mm:ss"}}</p> </div> </div>
JS代码:
javascript
function MyController($scope, $timeout) { var updateTime = function () { $scope.clock = { time: new Date() }; $timeout(function () { $scope.clock.time = new Date(); updateTime(); }, 1000); } updateTime(); }
对上面代码的解释:
我们通过上面的JS代码在HTML页面中创建了一个可以自动更新的时间,
我们先不去关心如何实现时间更新的,先看看我们是如何进行依赖注入的;我们在MyController
这个函数中设置的参数名称分别是$scope
和$timeout
所以在程序运行到这个函数的时候,AngularJS
会在内部调用函数对象的toString()
方法,假定参数的名称就是依赖的名称,分析并提取出函数的参数列表,然后通过$injector
将这些参数注入进对象的实例。
需要注意的地方:
- 与上述方法只在
AngularJS
的版本低于1.3.0时才可以成功运行。 - 这个方法只适合未经过压缩和混淆的代码,因为
AngularJS
需要原始未经压缩的参数列表来进行解析。
2. 显式的注入声明
AngularJS
提供了显式地方法来明确定义一个函数在被调用时需要用到的依赖关系。通过这种方法声明依赖,即使在源代码被压缩,参数名称发生改变的情况下依然可以正常工作。
下面是代码示例:
HTML代码:
html
<div ng-app> <div ng-controller="MyController"> <p ng-cloak>{{clock.time | date: "yyyy-MM-dd hh:mm:ss"}}</p> </div> </div>
JS代码:
javascript
function MyController(s, t) { var updateTime = function () { s.clock = { time: new Date() }; t(function () { s.clock.time = new Date(); updateTime(); }, 1000); } updateTime(); } MyController["$inject"] = ["$scope", "$timeout"];
对上面代码的解释:
我们给我们的函数设置的参数名称分别是s
和t
,然后我们在后面使用MyController["$inject"] = ["$scope", "$timeout"];
显式的将我们需要的依赖注入到MyController
函数中;所以在MyController
函数中,s
代表$scope
,t
代表$timeout
,是不是感觉不像听的那么难,我们继续我们的依赖注入。
需要注意的地方
- 对于这种声明方式来讲,参数的顺序是十分重要的,因为
$inject
数组元素的顺序必须和注入的参数的顺序一一对应。
上面的JS代码还有一种写法如下所示:
JS代码:
javascript
var MyControllerFactory = function MyController(s, t) { var updateTime = function () { s.clock = { time: new Date() }; t(function () { s.clock.time = new Date(); updateTime(); }, 1000); } updateTime(); } MyControllerFactory.$inject = ["$scope","$timeout"];
注意HTML代码中的ng-controller
需要改为MyControllerFactory
行内注入声明
AngularJS
提供的行内注入方法实际上是一种语法糖,它与前面的提到的通过$inject
属性进行声明的原理是一样的,但是允许我们在函数定义的时候从行内将参数传入,这种方法方便,简单,而且避免了在定义的过程中使用临时变量。
下面是代码示例:
HTML代码:
html
<div ng-app="MyApp"> <div ng-controller="MyController"> <p ng-cloak>{{clock.time | date: "yyyy-MM-dd hh:mm:ss"}}</p> </div> </div>
JS代码:
javascript
angular.module("MyApp", []) .controller("MyController", ["$scope", "$timeout", function (s, t) { var updateTime = function () { s.clock = { time: new Date() }; t(function () { s.clock.time = new Date(); updateTime(); }, 1000); } updateTime(); }]);
需要注意的地方,行内声明的方式允许我们直接传入一个参数数组,而不是一个函数。数组的元素是字符串,它们代表的是可以被注入到对象中的依赖名字,最后一个参数就是依赖注入的目标函数对象本身。
到这里已经把AngularJS
的依赖注入方法说得差不多了,不过还有一点,就是我们上面的方法都依赖一个服务,那就是$injector
,我后面会和大家一起来探讨一下这个东西到底是什么东西。
还有如果你觉得我上面所说的有些地方是不对的,还望指出来,我们一起进步^_^