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 相关文章推荐
利用ASP发送和接收XML数据的处理方法与代码
Nov 13 Javascript
javascript 事件查询综合 推荐收藏
Mar 10 Javascript
各种页面定时跳转(倒计时跳转)代码总结
Oct 24 Javascript
PHP abstract与interface之间的区别
Nov 11 Javascript
浅谈JavaScript中Date(日期对象),Math对象
Feb 05 Javascript
JavaScript框架是什么?怎样才能叫做框架?
Jul 01 Javascript
js实现仿Discuz文本框弹出层效果
Aug 13 Javascript
巧用jQuery选择器提高写表单效率的方法
Aug 19 Javascript
使用react实现手机号的数据同步显示功能的示例代码
Apr 03 Javascript
react的滑动图片验证码组件的示例代码
Feb 27 Javascript
JS实现简单tab选项卡切换
Oct 25 Javascript
详解vue3.0 的 Composition API 的一种使用方法
Oct 26 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
与空气斗智斗勇的经典《Overlord》,传说中的“无稽之谈”
2020/04/09 日漫
探讨Smarty中如何获取数组的长度以及smarty调用php函数的详解
2013/06/20 PHP
解析如何去掉CodeIgniter URL中的index.php
2013/06/25 PHP
PHP小偷程序的设计与实现方法详解
2016/10/15 PHP
thinkPHP自定义类实现方法详解
2016/11/30 PHP
PHP 访问数据库配置通用方法(json)
2018/05/20 PHP
PHP中rename()函数的妙用讲解
2019/02/28 PHP
laravel框架模板之公共模板、继承、包含实现方法分析
2019/08/30 PHP
javascript数据结构之二叉搜索树实现方法
2015/11/25 Javascript
Javascript字符串拼接小技巧(推荐)
2016/06/02 Javascript
angular框架实现全选与单选chekbox的自定义
2017/07/06 Javascript
jQuery选取所有复选框被选中的值并用Ajax异步提交数据的实例
2017/08/04 jQuery
深入理解Vuex 模块化(module)
2017/09/26 Javascript
动态加载权限管理模块中的Vue组件
2018/01/16 Javascript
Vue.js构建你的第一个包并在NPM上发布的方法步骤
2019/05/01 Javascript
elementui之el-tebs浏览器卡死的问题和使用报错未注册问题
2019/07/06 Javascript
微信小程序录音实现功能并上传(使用node解析接收)
2020/02/26 Javascript
python基础教程项目二之画幅好画
2018/04/02 Python
Python获取二维矩阵每列最大值的方法
2018/04/03 Python
使用pandas读取csv文件的指定列方法
2018/04/21 Python
Python 中的range(),以及列表切片方法
2018/07/02 Python
python输入一个水仙花数(三位数) 输出百位十位个位实例
2020/05/03 Python
python中get和post有什么区别
2020/06/19 Python
Python 程序报错崩溃后如何倒回到崩溃的位置(推荐)
2020/06/23 Python
Python configparser模块应用过程解析
2020/08/14 Python
Html5移动端网页端适配(js+rem)
2021/02/03 HTML / CSS
Nike荷兰官方网站:Nike.com (NL)
2018/04/19 全球购物
检察官就职演讲稿
2014/01/13 职场文书
客户接待方案
2014/02/26 职场文书
公司任命书模板
2014/06/06 职场文书
2015年语文教学工作总结
2015/05/25 职场文书
2016年禁毒宣传活动总结
2016/04/05 职场文书
《正面管教》读后有感:和善而坚定的旅程
2019/12/19 职场文书
MySQL Innodb关键特性之插入缓冲(insert buffer)
2021/04/08 MySQL
深入浅出讲解Java8函数式编程
2022/01/18 Java/Android
海康机器人重磅发布全新算法开发平台VM4.2
2022/04/21 数码科技