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面向对象之四 继承
Feb 08 Javascript
JavaScript高级程序设计 错误处理与调试学习笔记
Sep 10 Javascript
jQuery应用之jQuery链用法实例
Jan 19 Javascript
JavaScript程序中的流程控制语句用法总结
May 23 Javascript
javascript的几种写法总结
Sep 30 Javascript
jquery 动态增加删除行的简单实例(推荐)
Oct 12 Javascript
jQuery绑定事件的四种方式介绍
Oct 31 Javascript
vue .js绑定checkbox并获取、改变选中状态的实例
Aug 24 Javascript
vue中使用rem布局代码详解
Oct 30 Javascript
ES6 Symbol在对象中的作用实例分析
Jun 06 Javascript
JavaScript如何实现监听键盘输入和鼠标监点击
Jul 20 Javascript
解决vue页面刷新,数据丢失的问题
Nov 24 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
Discuz 5.0 中读取纯真IP数据库函数分析
2007/03/16 PHP
让CodeIgniter的ellipsize()支持中文截断的方法
2014/06/12 PHP
php中的观察者模式简单实例
2015/01/20 PHP
纯php生成随机密码
2015/10/30 PHP
javascript 网页跳转的方法
2008/12/24 Javascript
javascript学习笔记(五) Array 数组类型介绍
2012/06/19 Javascript
jquery获取css中的选择器(实例讲解)
2013/12/02 Javascript
jQuery中:visible选择器用法实例
2014/12/30 Javascript
基于BootStrap Metronic开发框架经验小结【二】列表分页处理和插件JSTree的使用
2016/05/12 Javascript
js判断主流浏览器类型和版本号的简单实现代码
2016/05/26 Javascript
用最简单的方法判断JavaScript中this的指向(推荐)
2017/09/04 Javascript
在vue中使用G2图表的示例代码
2019/03/19 Javascript
小程序getLocation需要在app.json中声明permission字段
2019/04/04 Javascript
js实现页面多个日期时间倒计时效果
2019/06/20 Javascript
基于layui轮播图满屏是高度自适应的解决方法
2019/09/16 Javascript
微信小程序实现签字功能
2019/12/23 Javascript
解决vue的touchStart事件及click事件冲突问题
2020/07/21 Javascript
[03:01]完美世界DOTA2联赛PWL S2 集锦第二期
2020/12/03 DOTA
Python函数式编程指南(一):函数式编程概述
2015/06/24 Python
python 捕获 shell/bash 脚本的输出结果实例
2017/01/04 Python
浅谈python内置变量-reversed(seq)
2017/06/21 Python
Python3 queue队列模块详细介绍
2018/01/05 Python
Python之pymysql的使用小结
2019/07/01 Python
django 框架实现的用户注册、登录、退出功能示例
2019/11/28 Python
Python异常原理及异常捕捉实现过程解析
2020/03/25 Python
Python run()函数和start()函数的比较和差别介绍
2020/05/03 Python
法国娇韵诗官方旗舰店:Clarins是来自法国的天然护肤品牌
2018/06/30 全球购物
世界上最大的皮肤科医生拥有和经营的美容网站:LovelySkin
2021/01/03 全球购物
Java里面有没有全局变量?为什么?
2015/02/06 面试题
怎样在程序里获得一个空指针
2015/01/24 面试题
光盘行动倡议书
2014/02/02 职场文书
促销活动总结报告
2014/04/26 职场文书
一分钟演讲稿
2014/04/30 职场文书
2015年幼儿园德育工作总结
2015/05/25 职场文书
深度学习tensorflow基础mnist
2021/04/14 Python
html,css,javascript是怎样变成页面的
2023/05/07 HTML / CSS