DI是Angular的特色功能,而在Angular 2.0的计划中,DI将成为一个独立的模块,参见 https://github.com/angular/di.js 这意味着它也有机会被用于nodejs等技术中,其他前端框架也完全有机会使用它。

DI简介

对于后端程序员,特别是java、.NET程序员来说,DI是个无须解释的概念,但是对前端程序员则比较陌生,我只简单的说一下DI的概念,然后分析用JS实现DI的原理,DI原理和优点的深入讲解参见 http://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC

假设你需要某个对象,你有几种选择?

方法一、自己创建它。

这是看起来最简单直观的方法,但这只是看起来很美,因为对象的创建有可能很复杂:需要一系列的参数,可能还需要先这样、再那样的一系列初始化操作,甚至可能还需要创建它的相关对象,相关对象还需要创建相关对象的相关对象……你确定你在写代码而不是择毛线吗?一句话,这种方式无法适应复杂的对象,随着对象的复杂化,自己动手丰衣足食已经不再是好的选择。

方法二、使用者从全局注册表查阅它。

这种方法轻松多了,对象的创建者要自己负责创建自己,然后把自己注册到某个全局注册表中,你需要它的时候,就去注册表中根据名字或者其它的独有信息(比如强类型语言中的类名)查询:obj = globalRegistry.get(objectId)。显然,轻松多了,再也不用管它的初始化参数和初始化流程了,拿来就用。那么,问题在哪里?问题在于它很难被单元测试。我们都知道,全局变量是单元测试中的魔鬼,因为它会让各个“单元”互相耦合在一起,那将是单元测试的噩梦。而globalRegistry就是一个全局变量,并且连累着其中的对象也都变成全局的了。更重要的是:你还不够懒。

方法三、衣来伸手,饭来张口。

这就是所谓DI了。也就是说,我只要指出我需要哪些对象,然后就有人会把这个对象给我,而这个“人”可能是一个应用框架(Framework),也可能是测试容器(Test Runner),我不会关心它是谁,也不用关心它怎么得到的这个对象。这个“人”,专业的说法叫“容器”,下面我会说到这个容器在Angular中对应哪个概念。

而我指出“我需要哪些对象”的方式,也有多种,比如可以直接声明一个属性,或者写个Annotation,或者写个配置文件来声明依赖关系,或者在函数的参数中声明,目前angular所采用的方式是函数的参数形式,和一种变形的Annotation形式来防止minify破坏名称,据说angular 2.0会使用新的语言特性强化annotation的方式。

JavaScript中实现DI的原理

在JavaScript中实现DI,看起来难,实际上原理很简单,它的核心技术是Function对象的toString()。我们都知道,对一个函数对象执行toString(),它的返回值是函数的源码,知道了这一点,接下来就简单的:我获取了函数源码,然后我对函数的声明进行解析,伪码如下:

var giveMe = function(config) {
}; var registry = {};
var inject = function(func, thisForFunc) {
// 获取源码
var source = func.toString();
// 用正则表达式解析源码
var matcher = source.match(/..表达式有些复杂,省略../);
// 解析结果是各个参数的名称
var objectIds = ....
// 查阅出相应的对象,放到数组中准备作为参数传过去
var objects = [];
for (var i = 0; i < objectIds.length; ++i)
objects.push(registry[objectIds[i]]);
// 调用这个函数,并且把参数传过去
func.apply(thisForFunc || func, objects)
}; inject(giveMe)

当然,一个实际的DI系统需要考虑的问题比这要多很多,但是这段代码用来表现原理应该足够了。

Angular中的DI

接下来我们再来看Angular中的DI实现:

在Angular中,所有主要编程元素都需要通过某种方式注册进去,比如myModule.service('serviceName', function().... 这实际上就是把后面这个函数加入到一个容器中,要注意的是:angular全面实现了延迟初始化,也就是说,当这个对象没有被别人需要的时候,它是不会被创建的,这样对于提高性能有一定的帮助,特别是加快了启动速度。

这里一个有趣的问题是:Angular的容器是什么。Angular不存在真正的全局对象,所以你可以放心的在同一个页面中放多个app,而不用担心他们互相干扰,但是容器又需要一个众所周知的地方来存放这些“名字和对象”的注册表(Registry),在Angular中,这个注册表就叫做module,所以,现在你应该知道为什么module的地位很重要了吧?不过一个app中可以存在很多不同名字的module,它们之间存在某些依赖关系,而这体现在module的声明语法中:angular.module('someModule', ['dep1', 'dep2]),这样划分module有利于程序的文件组织。

根据DI的原理,一个自然的推论就是:被注入的对象通常都是单例,因为创建了一个,就可以始终使用它了,不需要多次创建。因此,如果你需要在angular中跨controller共享数据或者通讯,那么你可以创建一个service/value/constant等,然后把它们分别注入到两个controller中,而这两个controller将自然而然的共享同一个对象。

欢迎转载,转载请注明作者和出处:http://www.cnblogs.com/asnowwolf/p/3684700.html

原创:Javascript DI!Angular依赖注入的实现原理的更多相关文章

  1. Yii2.0 依赖注入(DI)和依赖注入容器的原理

    依赖注入和依赖注入容器 为了降低代码耦合程度,提高项目的可维护性,Yii采用多许多当下最流行又相对成熟的设计模式,包括了依赖注入(Denpdency Injection, DI)和服务定位器(Serv ...

  2. 30行代码让你理解angular依赖注入:angular 依赖注入原理

    依赖注入(Dependency Injection,简称DI)是像C#,java等典型的面向对象语言框架设计原则控制反转的一种典型的一种实现方式,angular把它引入到js中,介绍angular依赖 ...

  3. angular 依赖注入原理

    依赖注入(Dependency Injection,简称DI)是像C#,java等典型的面向对象语言框架设计原则控制反转的一种典型的一种实现方式,angular把它引入到js中,介绍angular依赖 ...

  4. Angular依赖注入详解

    Angular算是将后端开发工程化引入前端的先驱之一,而Dependency injection依赖注入(后面简称为DI)又是Angular内部运作的核心功能,所以要深入理解Angular有必要先理解 ...

  5. JavaScript里的依赖注入

    JavaScript里的依赖注入 我喜欢引用这句话,“程序是对复杂性的管理”.计算机世界是一个巨大的抽象建筑群.我们简单的包装一些东西然后发布新工具,周而复始.现在思考下,你所使用的语言包括的一些内建 ...

  6. [译] 关于 Angular 依赖注入你需要知道的

    如果你之前没有深入了解 Angular 依赖注入系统,那你现在可能认为 Angular 程序内的根注入器包含所有合并的服务提供商,每一个组件都有它自己的注入器,延迟加载模块有它自己的注入器. 但是,仅 ...

  7. [译]javascript中的依赖注入

    前言 在上文介绍过控制反转之后,本来打算写篇文章介绍下控制反转的常见模式-依赖注入.在翻看资料的时候,发现了一篇好文Dependency injection in JavaScript,就不自己折腾了 ...

  8. Angular依赖注入:全面讲解(翻译中)

    在实际使用Angular依赖注入系统时,你需要知道的一切都在本文中.我们将以实用易懂并附带示例的形式解释它的所有高级概念. Angular最强大.最独特的功能之一就是它内置的依赖注入系统. 大多数时候 ...

  9. 理论+案例,带你掌握Angular依赖注入模式的应用

    摘要:介绍了Angular中依赖注入是如何查找依赖,如何配置提供商,如何用限定和过滤作用的装饰器拿到想要的实例,进一步通过N个案例分析如何结合依赖注入的知识点来解决开发编程中会遇到的问题. 本文分享自 ...

随机推荐

  1. 维多利亚的秘密 golang入坑系列

    原文在gitbook,字字原创,版权没有,转载随意. 在写本文的前一天,2017维密在上海开始了. 为了纪念屌丝界的盛世,特为本节起名维多利亚的秘密.现在的社会,要想出名只有抓眼球.所以写份技术文章, ...

  2. QT Creator常用快捷键

    1.F10,F11 单步调试 2.Ctrl + / :注释/取消注释选定内容. 3.F4 :在 头文件(.h) 和 实现文件(.cpp) 之间进行切换. 4.Ctrl + i :自动格式化选中代码. ...

  3. Bootstrap实现的页面

    实现的效果如图,使用bootstrap需要至少三个文件 去bootstrap网上下载,然后使用这三个文件可以了 使用方式,通过标签,class命名来引用已经定制好的html样式 <!DOCTYP ...

  4. python全栈开发从入门到放弃之列表的内置方法

    1.列表切片 l=['a','b','c','d','e','f'] print(l[1:5]) # 根据索引号来切片,但顾头不顾尾 ['b', 'c', 'd', 'e'] print(l[1:5: ...

  5. JSON 弹窗

    JSON和AJAX <script type="text/javascript"> $(document).ready(function(e) { var a = { ...

  6. Struct2小组开发简单命名规范

    基本原则或者说理念:简单就是美 1.数据库名:项目名 2.表名:_model名 3.字段:和model中的属性名一致(不要和数据库名冲突) 4.用层来划分包:com.liying.bbs.action ...

  7. redis 笔记02 对象、数据库

    对象 Redis并没有使用之前介绍的数据结构来实现键值对数据库,而是基于那些数据结构创建了一个对象系统,这个系统包含字符串对象.列表对象.哈希对象.集合对象和有序集合对象这五种类型对象, 每种对象都用 ...

  8. linux新手学习之Arch Linux入门经验分享

    我一直是以Ubuntu与Fedora作为新手入门的系统,但是其实我真正想推荐的是Arch,经过前面的学习,或许你对Linux已经有了一个大致的了解,现在如果你想加速你的步伐,也许可以看看本文.如果要问 ...

  9. Oracle索引以及索引碎片

    索引,可以增加查询速度,若没有索引,每次查询都必须是全表查询.例如,搜索某个记录时(如name="gdpuzxs")时,需要全表扫描一下,因为不知道有多少个name="g ...

  10. Spring -- aop(面向切面编程),前置&后置&环绕&抛异常通知,引入通知,自动代理

    1.概要 aop:面向方面编程.不改变源代码,还为类增加新的功能.(代理) 切面:实现的交叉功能. 通知:切面的实际实现(通知要做什么,怎么做). 连接点:应用程序执行过程期间,可以插入切面的地点. ...