最开始使用angular的时候,总是觉得它的依赖注入方式非常神奇。

如果你跳槽的时候对新公司说,我曾经使用过angular,那他们肯定会问你angular的依赖注入原理是什么?

这篇博客其实是angular源码阅读之路的一个必经站点,就是要理解injector,provider,module之间的关系——这关系其实就是依赖注入的本质。

那么请专注地看下面这一段话吧:

通俗一点的理解:

module是发布任务的BOSS。

injector是领取任务的中间人。

provider是真正去执行任务的马仔。

当然上面这一段话只是比喻,不太严谨,可是很形象。待我慢慢解释来。

如果你比较熟悉angular,那么你肯定知道在每一个module对象上,都有一个私有属性"_invokeQueue"。

这个_invokeQueue,其实就是module发布的任务。

怎么理解『_invokeQueue,其实就是module发布的任务。』这句话呢?请看下面的简单小代码。

当我执行下面这段语句,我会在myapp中创建一个全局变量name='不咬人的蚊子':

//注册了一个全局变量name='不咬人的蚊子'
angular.module('myapp').constant('name','不咬人的蚊子');

而这个变量'name'我可以在controller里面这样使用:

angular.module.controller('myctr',['$scope','name',function($scope,name){
console.log(name)//不咬人的蚊子
$scope.name = name;
}])

现在说回_invokeQueue,当我执行了那个注册全局变量的constant方法的时候,其实是module发布了一个任务,这个任务保存在_invokeQueue里面。

注意:其实这时候只是发布任务,任务并没有被执行。这时候_invokeQueue里面是这样的:

module._invokeQueue=[
['constant',['name','不咬人的蚊子']]//数组里面包含着另一个数组。
]

对,没错,这就是Module发布的任务,invokeQueue其实就是一个数组,里面有着一系列任务(这里只是拿constant举例,其实在真实案例中,还会有各种任务,比如controller啊什么的)。

invokeQueue这个数组里面的每一个元素都是一个任务,如你所见,这任务也是一个数组。

任务数组的第1个元素(下标为0)记录了这个任务具体是什么任务,是constant,还是controller,还是directive等等。

任务数组的第2个元素(下标为1)记录了执行任务需要的参数。

注意注意,这里我们为了易于理解,只拿constant举例子,以后慢慢复杂起来,会越来越丰富。

注意注意,module发布了任务以后,只是发布了,并没有执行。

那么什么时候执行呢?

当angular一个app启动的时候,会自动生成一个injector,也就是大家口中的注射器,这是一个对象,这个injector对象会读取module中的各种任务。

比如injecotr读取module的invokeQueue之后,发现了第一条任务:

 ['constant',['name','不咬人的蚊子']]

于是injector就会发现,这是一个constant任务,参数是name,'不咬人的蚊子'。

injector并不能处理constant任务,所以它去找一个名为constant的provider,这个provider可以提供一个函数,这个函数正好接收两个参数。

于是injector把任务中的两个参数(也就是name和'不咬人的蚊子'这两个参数)交给constantProvider,让它来执行。

好了,这就是一个口头能讲明白的原理。那么angularJs是如何实现这个机制的呢?我打算把简单版的代码贴在下面,如果感兴趣的同学可以看看,如果不感兴趣的同学其实只要把上面的文字给看明白了,下面的代码随便看个乐呵就行。(这个代码可能会有部分是接着上一篇博客的代码,如果看着不知道怎么回事,可以看看上一篇博客。) 

setupModuleLoader.js

function setupModuleLoader(window){
var ensure=function(obj,name,factory){
return obj[name]||(obj[name]=factory())
}
var angular = ensure(window,'angular',Object); var createModule = function(name,requires){
var invokeQueue=[];//增加一个任务队列
var moduleInstance = {
name:name,
requires:requires,
_invokeQueue:invokeQueue,
//constant方法的实质是向invokeQueue数组里面增加一个任务
constant:function(key,value){
invokeQueue.push(['constant',[key,value]])
},
};
return moduleInstance;
} ensure(angular,'module',function(){
var modules={};
return function(name,requires){
if(requires){
return createModule(name,requires,modules)
}else{
return getModule(name,modules);
}
}
})
}

createInjector.js

//createInjector(['app1','app2'])
//参数是一个字符串或者一个数组,内容是module名
function createInjector(modulesToLoad){
//cache用来缓存一些一直可以用到的值
var cache={}; $provide={
constant:function(key,value){
cache[key]=value;
}
} //这里的foreach方法并不是一个真正能运行的foreach,能看懂就行了
//每次APP启动的时候,injector都会按照传入的module名来遍历所有module
//这样就可以得到所有module发布的任务,并且一一执行这些任务
$.forEach(modulesToLoad,function(moduleName){
var module = window.angular.module(moduleName);
$.forEach(module._invokeQueue,function(invokeArgs){
var method=invokeArgs[0];
var args = invokeArgs[1];
$provide[method].apply($provide,args);
})
})
return {
has:function(key){
return cache.hasOwnProperty(key)
},
get:function(key){
return cache[key]
}
}
}

如果你耐着性子看到了这里,并且思路还算清晰,那么你肯定会问,现在injector执行了所有任务,并且把一切东西都准备好了,那么我们使用这些数据的途径和方法是什么呢?哈哈,这个别急,很快会解释明白,但是现在起码我们对依赖注入有了一个很好的理解了不是么?冬天来了,春天不会远了。

  

 

  

angular源码阅读,依赖注入的原理:injector,provider,module之间的关系。的更多相关文章

  1. Spring源码剖析依赖注入实现

    Spring源码剖析——依赖注入实现原理 2016年08月06日 09:35:00 阅读数:31760 标签: spring源码bean依赖注入 更多 个人分类: Java   版权声明:本文为博主原 ...

  2. angular源码阅读的起点,setupModuleLoader方法

    angular源码其实结构非常清晰,划分的有条有理的,大概就是这样子: (function(window,document,jquery,undefined){ //一些工具函数 //EXPR 编译器 ...

  3. angular源码阅读3:真的,依赖注入的原理

    前面已经提到了: 如何注册一个module. 如何获取一个module. injector与module以及provider的关系. 那么已经剩下最后一部分了,就是关于依赖是如何被注入的. 且看下面这 ...

  4. Yii2.0源码阅读-behavior的实现原理

    Yii2.0中的一个思想就是组件化的思想,所以.大多数的类都直接或间接的继承自yii\base\Component,而组件的三大功能:属性.事件.行为. 行为的目的是为了方便的扩展一个类的功能,而不需 ...

  5. ovs源码阅读--流表查询原理

    背景 在ovs交换机中,报文的处理流程可以划分为一下三个步骤:协议解析,表项查找和动作执行,其中最耗时的步骤在于表项查找,往往一个流表中有数目巨大的表项,如何根据数据报文的信息快速的查找到对应的流表项 ...

  6. angular源码分析:angular中的依赖注入式如何实现的

    一.准备 angular的源码一份,我这里使用的是v1.4.7.源码的获取,请参考我另一篇博文:angular源码分析:angular源代码的获取与编译环境安装 二.什么是依赖注入 据我所知,依赖注入 ...

  7. angular源码分析:injector.js文件分析——angular中的依赖注入式如何实现的(续)

    昨天晚上写完angular源码分析:angular中jqLite的实现--你可以丢掉jQuery了,给今天定了一个题angular源码分析:injector.js文件,以及angular的加载流程,但 ...

  8. Kubernetes 学习(八)Kubernetes 源码阅读之初级篇------源码及依赖下载

    0. 前言 阅读了一段时间 Golang 开源代码,准备正式阅读 Kubernetes 项目代码(工作机 Golang 版本为 Go 1.12) 参照 <k8s 源码阅读> 选择 1.13 ...

  9. python3 源码阅读-虚拟机运行原理

    阅读源码版本python 3.8.3 参考书籍<<Python源码剖析>> 参考书籍<<Python学习手册 第4版>> 官网文档目录介绍 Doc目录主 ...

随机推荐

  1. Ubuntu 14.04 编译安装 boost 1.58

    简介 Boost is a set of libraries for the C++ programming language that provide support for tasks and s ...

  2. find命令的拾遗

    find -maxdepth 1 -regextype posix-extended -type f -regex "\./${name}[-\.].*\.(gz|bz2|tgz|zip|t ...

  3. Java提高篇——Java 异常处理

    异常的概念 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的. 比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error:如果你用Syst ...

  4. python gevent 协程

    简介 没有切换开销.因为子程序切换不是线程切换,而是由程序自身控制,没有线程切换的开销,因此执行效率高, 不需要锁机制.因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断 ...

  5. lua table integer index 特性

    table.maxn (table) Returns the largest positive numerical index of the given table, or zero if the t ...

  6. 把 MWeb Lite 的文档库文档和数据搬到 MWeb 正式版中

    MWeb Lite 版的文档库中的文档要搬到 MWeb 正式版中,如果 Lite 版的文档中没有图片或者只有少量图片,可以用导入导出为 Markdown 的方法. 否则的话请用以下方式(注意下面这个方 ...

  7. python爬虫

    预:网页的组成 HTML(结构)+css(样式)+javascript(功能) 爬虫主要针对的是HTML和css HTML: <div></div>div标签 代表网页中某个区 ...

  8. Node.js Express 框架 POST方法

    POST 方法 以下实例演示了在表单中通过 POST 方法提交两个参数,我们可以使用 server.js 文件内的 process_post 路由器来处理输入: index.htm 文件代码修改如下: ...

  9. Jquery插件Ztree使用所遇问题

    问题1.$.fn.zTree为空或为Undefined 我在MVC中引用Jquery插件Ztree的JS并不存任何问题,而当我将Ztree的js引入项目中,就出现$.fn.zTree为空或为Undef ...

  10. IFRAM随内部长宽高变化

    <iframe src="" id="iframe_CustomerVisitRecord" width="700" height=& ...