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 相关文章推荐
jquery 图片截取工具jquery.imagecropper.js
Apr 09 Javascript
获取非最后一列td值并将title设为该值的方法
Oct 30 Javascript
javascript记住用户名和登录密码(两种方式)
Aug 04 Javascript
关于JavaScript作用域你想知道的一切
Feb 04 Javascript
vue.js入门教程之绑定class和style样式
Sep 02 Javascript
基于jquery实现多选下拉列表
Aug 02 jQuery
Vue.js 的移动端组件库mint-ui实现无限滚动加载更多的方法
Dec 23 Javascript
浅谈AngularJS中$http服务的简单用法
May 15 Javascript
详解在vue-test-utils中mock全局对象
Nov 07 Javascript
js判断一个对象是数组(函数)的方法实例
Dec 19 Javascript
Vue实现返回顶部按钮实例代码
Oct 21 Javascript
如何优化vue打包文件过大
Apr 13 Vue.js
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
Netflix将与CLAMP、乙一以及冲方丁等6名知名制作人合伙展开原创动画计划!
2020/03/06 日漫
Array of country list in PHP with Zend Framework
2011/10/17 PHP
php中explode与split的区别介绍
2012/10/03 PHP
PHP的serialize序列化数据以及JSON格式化数据分析
2015/10/10 PHP
Symfony2 session用法实例分析
2016/02/04 PHP
javascript英文日期(有时间)选择器
2007/05/02 Javascript
Web前端设计模式  制作漂亮的弹出层
2010/10/29 Javascript
关于js注册事件的常用方法
2013/04/03 Javascript
原生JS实现加入收藏夹的代码
2013/10/24 Javascript
自己写的Javascript计算时间差函数
2013/10/28 Javascript
jQuery实现流动虚线框的方法
2015/01/29 Javascript
jQuery仿天猫实现超炫的加入购物车
2015/05/04 Javascript
js将字符串中的每一个单词的首字母变为大写其余均为小写
2017/01/05 Javascript
Ionic+AngularJS实现登录和注册带验证功能
2017/02/09 Javascript
jQuery实现返回顶部按钮和scroll滚动功能[带动画效果]
2017/07/05 jQuery
JS 封装父页面子页面交互接口的实例代码
2019/06/25 Javascript
解决layui table表单提示数据接口请求异常的问题
2019/09/24 Javascript
jquery实现垂直手风琴导航栏
2020/02/18 jQuery
基于js实现逐步显示文字输出代码实例
2020/04/02 Javascript
vue 解决IOS10低版本白屏的问题
2020/11/17 Javascript
Python远程桌面协议RDPY安装使用介绍
2015/04/15 Python
Python中map和列表推导效率比较实例分析
2015/06/17 Python
Python selenium 父子、兄弟、相邻节点定位方式详解
2016/09/15 Python
python训练数据时打乱训练数据与标签的两种方法小结
2018/11/08 Python
基于python生成英文版词云图代码实例
2020/05/16 Python
python语言是免费还是收费的?
2020/06/15 Python
python中np是做什么的
2020/07/21 Python
印度和世界各地的精美产品:Ikka Dukka
2018/02/12 全球购物
Linux管理员面试经常问道的相关命令
2013/04/29 面试题
网络工程专业毕业生推荐信
2013/10/28 职场文书
公司聘任书模板
2014/03/29 职场文书
岗位安全生产责任书
2014/07/28 职场文书
《地震中的父与子》教学反思
2016/02/16 职场文书
意外事故赔偿协议书
2016/03/22 职场文书
go语言基础 seek光标位置os包的使用
2021/05/09 Golang
Redis如何实现分布式锁
2021/08/23 Redis