AngularJs Dependency Injection(DI,依赖注入)


Posted in Javascript onSeptember 02, 2016

一、Dependency Injection(依赖注入)

依赖注入(DI)是一个软件设计模式,处理代码如何得到它所依赖的资源。

关于DI更深层次的讨论,可以参观Dependency Injection(http://en.wikipedia.org/wiki/Dependency_injection),Inversion of Control(http://martinfowler.com/articles/injection.html),也可以参观软件设计模式的书。

1. DI in a nutshell(简说DI)

object或者function,只能够通过以下三种方式获取他们依赖的资源:

1) 可以通过new运算符创建依赖的资源。

2) 可以通过全局变量查找依赖的资源。

3) 可以通过参数传入依赖的资源。

1、2两种方式,并不是最佳的,因为它们对依赖关系进行hard code,这使得修改依赖关系时,不是不可能,但会变得比较复杂。这对于测试来说尤其是个问题,通常在独立测试时,希望能够提供模拟的依赖资源。

第3种方法相对来说最可行,因为它去除了从组件(component)中定位依赖的责任。依赖仅仅交给组件就可以了。

function SomeClass(greeter) {
   this.greeter = greeter
}

SomeClass.prototype.doSomething = function(name) {
   this.greeter.greet(name);
}

上面的例子,SomeClass不用关心定位greeter这个依赖,它仅仅在运行时传递greeter。

这样是比较合适的,但它将获取依赖资源的责任交给了负责构建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。

二、Dependency Annotation(依赖注释,说明依赖的方式)

injector如何知道什么服务需要被注入呢?

应用开发者需要提供被injector用作解决依赖关系的注释信息。所有angular已有的API函数,都引用了injector,每一个文档中提及的API都是这样。下面是用服务名称信息注释我们的代码的三个等同的方法。

1. Inferring Dependencies(隐含依赖)

这是获取依赖资源的最简单的方式,但需要假定function的参数名称与依赖资源的名称一致。

function MyController($scope, greeter) {
   ...
}

函数的injector,可以通过检查函数定义并提取函数名称,猜测需要注入的service的名称(functionName.toString(),RegExp)。在上面的例子中,$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'];

需要小心的是,$inject的顺序需要与函数声明的参数顺序保持一致。

这个注释方法,对于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) {
   ...;
}]);

记住,所有注释风格都是等价的,可以被用在支持injection的angular中的任何地方。

三、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() {
   ...
}

2. Factory methods

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) {
  
...
}]);

      以上就是对AngularJS Dependency Injection 的资料整理后续继续补充,谢谢大家对本站的支持!

Javascript 相关文章推荐
javascript 一个自定义长度的文本自动换行的函数
Aug 19 Javascript
Javascript this关键字使用分析
Oct 21 Javascript
JavaScript replace(rgExp,fn)正则替换的用法
Mar 04 Javascript
JS target与currentTarget区别说明
Aug 28 Javascript
学习从实践开始之jQuery插件开发 菜单插件开发
May 03 Javascript
javascript实现简单的鼠标拖动效果实例
Apr 10 Javascript
微信小程序 wx.request(object) API详解及实例代码
Sep 30 Javascript
谈谈对vue响应式数据更新的误解
Aug 01 Javascript
简单的网页广告特效实例
Aug 19 Javascript
vue实现简单的日历效果
Sep 24 Javascript
微信小程序使用 vant Dialog组件的正确方式
Feb 21 Javascript
JS关闭子窗口并且刷新上一个窗口的实现示例
Mar 10 Javascript
js实现String.Fomat的实例代码
Sep 02 #Javascript
在Docker快速部署Node.js应用的详细步骤
Sep 02 #Javascript
CSS3 3D 技术手把手教你玩转
Sep 02 #Javascript
js实现StringBuffer的简单实例
Sep 02 #Javascript
纯JS实现可拖拽表单的简单实例
Sep 02 #Javascript
vue.js入门教程之绑定class和style样式
Sep 02 #Javascript
js绘制购物车抛物线动画
Nov 18 #Javascript
You might like
一个ORACLE分页程序,挺实用的.
2006/10/09 PHP
php自动加载的两种实现方法
2010/06/21 PHP
php 删除一个数组中的某个值.兼容多维数组!
2012/02/18 PHP
php获取文件大小的方法
2014/02/26 PHP
PHP实现广度优先搜索算法(BFS,Broad First Search)详解
2017/09/16 PHP
解决laravel资源加载路径设置的问题
2019/10/14 PHP
js中substr,substring,indexOf,lastIndexOf的用法小结
2013/12/27 Javascript
JavaScript作用域链示例分享
2014/05/27 Javascript
JS+CSS实现电子商务网站导航模板效果代码
2015/09/10 Javascript
AngularJS ngModel实现指令与输入直接的数据通信
2016/09/21 Javascript
ionic2 tabs 图标自定义实例
2017/03/08 Javascript
详解nodejs操作mongodb数据库封装DB类
2017/04/10 NodeJs
jQuery validata插件实现方法
2017/06/25 jQuery
ionic 3.0+ 项目搭建运行环境的教程
2017/08/09 Javascript
JavaScript 数组的进化与性能分析
2017/09/18 Javascript
jQuery选择器之表单元素选择器详解
2017/09/19 jQuery
jQuery选择器之子元素过滤选择器
2017/09/28 jQuery
jQuery实现获取及设置CSS样式操作详解
2018/09/05 jQuery
JS根据Unix时间戳显示发布时间是多久前【项目实测】
2019/07/10 Javascript
Javascript原生ajax请求代码实例
2020/02/20 Javascript
ES6箭头函数和扩展实例分析
2020/05/23 Javascript
Python自动化测试工具Splinter简介和使用实例
2014/05/13 Python
改进Django中的表单的简单方法
2015/07/17 Python
Python有序字典简单实现方法示例
2017/09/28 Python
Python用for循环实现九九乘法表
2018/05/31 Python
Numpy array数据的增、删、改、查实例
2018/06/04 Python
改变 Python 中线程执行顺序的方法
2020/09/24 Python
详解scrapy内置中间件的顺序
2020/09/28 Python
python温度转换华氏温度实现代码
2020/12/06 Python
一款利用css3的鼠标经过动画显示详情特效的实例教程
2014/12/29 HTML / CSS
北京天润融通.net面试题笔试题
2012/02/20 面试题
经贸日语专业个人求职信
2013/12/13 职场文书
运动会方队口号
2014/06/07 职场文书
2015年超市工作总结
2015/04/09 职场文书
民间借贷纠纷答辩状
2015/08/03 职场文书
python中pandas对多列进行分组统计的实现
2021/06/18 Python