最开始使用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. 使用crypto模块实现md5加密功能(解决中文加密前后端不一致的问题)

    正常情况下使用md5加密 var crypto = require('crypto'); var md5Sign = function (data) { var md5 = crypto.create ...

  2. 使用天天模拟器开发Android应用

    自带的模拟器太慢,Genymotion配置过于复杂,天天模拟器旧版本直接可用于调试,由于新版本的天大模拟器端口号被修改为6555,要想用于开发,需要使用ADB命令进行连接. 下载天天模拟器 天天模拟器 ...

  3. a 添加href后当前栏目如何高亮显示

    //nav $(".nav li a").each(function() { $this = $(this); if ($this[0].href == String(window ...

  4. C语言第7次作业

    1 #include<stdio.h> int main() { char name[50];int character[26]={0};int i=0,j;int length=0;wh ...

  5. Spting--DI/IOC

    DI/IOC <bean> 代表由容器构建的对象(通过反射构建,且类必须有无参的构造方法)  公共属性 id="唯一的id" 在容器中是唯一的 name="类 ...

  6. 自己写一个swap函数交换任意两个相同类型元素的值 对空指针的使用 字节大小的判断(二)了解原理

    验证的代码: #include <stdio.h> int main(){ char c = 'z'; ) + (c << ) + () + 'a'; printf(" ...

  7. 自定义cell侧滑删除

    - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { return Y ...

  8. linux centos service 参数详解

    Service文件 开门见山,直接来看两个实际的服务配置文件吧. 第一个配置是 CoreOS 系统中 Docker 服务的 Unit 文件,路径是 /usr/lib/systemd/system/do ...

  9. linux 配置 ftp

    1.检测是否安装ftp 2.apt-get install 安装 (如果安装失败 执行apt-get install update<如果update失败 配置dns让自己的服务器 可以ping ...

  10. Spring.Net 初探之牛刀小试

    又是一个周末,感受着外面30°的高温,果断宅在家里,闲来无事,就研究了一下spring .net 框架, 在这里不得不说 vs2013确实是一个强大的开发工具(起码对于.net开发来说是这样的),哈哈 ...