前面已经提到了:

如何注册一个module。

如何获取一个module。

injector与module以及provider的关系。

那么已经剩下最后一部分了,就是关于依赖是如何被注入的。

且看下面这段代码。

//如你所见,注册了一个moduleA
//又如你所见,给moduleA上面注册了两个全局变量,a和b
angular.module('moduleA',[]);
angular.module('moduleA').constant('a',1);
angular.module('moduleA').constant('b',2); //生成一个注射器
var injector = createInjector('moduleA'); //定义一个函数,接收两个数作为参数,打印两个数的和
function add(a,b){
console.log(a+b);
}

函数声明好了,moduleA上面也有值了,那么怎么把moduleA上面的值作为函数的参数传进来呢?熟悉angular的小伙伴肯定知道有如下几种办法:

//第一种,给函数加$inject属性
add.$inject=['a','b'];
add(a,b)//3 //第二种,数组形式
['a','b',add(a,b)]; //第三种,直接用
add(a,b);

这次主要看第一种和第二种的原理,第三种的原理有点复杂,留着下一次说。

还记得之前的那个createInjector函数么?这个函数接收一些module名称作为参数,返回一个对象就是injector对象。当createInjector运行的时候,会把所有module都遍历一次,把module的_invokeQueue里面的任务都找到相应的provider执行一下,然后把各个module里面注册的东西都保存一份。

而依赖注入的本质就是——当你执行函数A的时候,根据函数A的$injector属性,或者函数A的数组元素(比如 ['a','b',add(a,b)]这种),去自己保存的那堆东西里面找到相应的数据,把这个数据传给函数进行调用。

来,配上源代码,别看代码长,其实都是上一篇博客的代码,这次只往里面加了一小部分。

//createInjector(['app1','app2'])
//参数是一个字符串或者一个数组,内容是module名
function createInjector(modulesToLoad) {
//cache用来缓存一些一直可以用到的值
var cache = {}; //所有加载过的module都记录在这个对象里面
var loadedModules = {}; $provide = {
constant: function(key, value) {
cache[key] = value;
}
} //这里的foreach方法并不是一个真正能运行的foreach,能看懂就行了
//每次APP启动的时候,injector都会按照传入的module名来遍历所有module
//这样就可以得到所有module发布的任务,并且一一执行这些任务
$.forEach(modulesToLoad, function loadModule(moduleName) {
//查询一下记录,如果没有加载过,再去执行
if (!loadedModules[moduleName]) {
var module = window.angular.module(moduleName);
//每加载一个模块,就操作一下loadedModules,把这个模块信息放进去
loadedModules[moduleName]=true;
//每次先把所有依赖的模块加载进去
$.forEach(module.requires,loadModule);
//然后再去调用这个模块的invokeQueue
$.forEach(module._invokeQueue, function(invokeArgs) {
var method = invokeArgs[0];
var args = invokeArgs[1];
$provide[method].apply($provide, args);
})
}
}) //invoke方法用来调用函数
function invoke(fn){
//把fn.$inject这个数组里面的元素拿出来,args是一个新的数组
var args=$.map(fn.$inject,function(token){
return cache[token];
})
fn.apply(null,args);
} return {
has: function(key) {
return cache.hasOwnProperty(key)
},
get: function(key) {
return cache[key]
},
invoke:invoke
}
}

看到那个invoke方法了么?来先顺一下思路,当createInjector函数加载了moduleA之后,返回了injector的时候,在自己的内部存储的cache里面已经是这样的:

cache={
a:1,
b:2
}

然后给返回的injector增加了一个invoke方法,这个方法是用来调用函数的。代码里看的应该很清楚,invoke方法接收一个函数作为参数,然后去寻找这个函数的$injector属性,这个属性里发现了a,b两个值,于是就去cache里面找key值为a,b的两个值,然后成为一个数组args,在用fn.apply(null,args)调用函数,这样这两个之就成为函数的参数传进去了。  

关于apply这个方法,简单说一句,第一个参数是函数的this值,第二个数组是函数的参数。有兴趣看源码的童鞋应该对这个不陌生,我就不啰嗦了。

总之,这样一来,就把module里面的值注入给函数了。注意哦,调用函数是Injector这个对象调用的:

injector.invoke(add)//调用add函数

那么问题来了,既然给函数增加$inject属性可以注入依赖了,那么数组形式的调用怎么实现呢?其实这个也很简单。

回想一下,其实依赖注入的本质是通过一个key值,从cache对象里面拿到相应的数据,对吧?

在injector里面还有一个方法,叫做 annotate,中文意思是解剖,这个方法的作用就是把函数的依赖给解剖出来:比如这篇文章的例子,我们是通过$inject属性,把a,b两个key值拿出来的。看代码:

//数组形式依赖注入
['a','b',function add(a,b){}]; //直接调用的依赖注入
function (a,b){} //annonate方法的作用就是把上面两种形式里面的a,b这两个键值拿出来

其实对于数组形式依赖注入来说,键值很好拿,拆一下数组就行了。我就贴一下annotate的简单代码,很好懂的:

function annotate(fn){
//如果是一个数组 ['a','b',function(a,b){}]
//返回数组最后一个函数之前的所有部分
if(isArray(fn)){
return fn.slice(0,fn.length-1);
}else{
//如果不是数组,就返回这个函数的$inject属性
return fn.$inject;
}
}

就是这么简单!

好了,其实依赖注入更牛逼的是那个直接调用的依赖注入,这个篇幅比较长,我打算明天再更。

晚安了兄弟们。欢迎留言和我交流呀 :)

  

  

  

  

最新文章

  1. BZOJ 1087 【SCOI2005】 互不侵犯King
  2. 开发一个支持多用户在线的FTP程序
  3. android 实现模拟按键
  4. textarea中的文字自动换行问题
  5. 最新Connectify注冊码(序列号) Connectify3.7序列号 破解版
  6. frameset iframe用来分页
  7. PHPCMS v9 导航显示二级菜单,显示相邻栏目,内容页显示二级栏目
  8. (转)12款开源JavaScript库
  9. 高级UNIX环境编程5 标准IO库
  10. ASP.NET MVC (Razor)开发
  11. CentOS 6.2 安装vsftpd 服务器(转)
  12. ASP.NET/MVC 配置log4net启用写错误日志功能
  13. C# WPF 使用委托修改UI控件
  14. git pull遇到错误:error: Your local changes to the following files would be overwritten by merge:
  15. 洛谷P3980 志愿者招募
  16. 19.1-uC/OS-III内存管理应用
  17. postman(八):使用newman来执行postman脚本
  18. QHBoxLayout移除控件
  19. 跨域导致无法获取cookie
  20. JBOSS Spring Web

热门文章

  1. (转载)在Visual Studio 2015中使用Git
  2. 分享 rabbitMQ入门详解
  3. FTP客户端连接时中文乱码问题处理
  4. ssh 配合 tar 实现远程推送
  5. 浅析 Magento网站建站空间的选择
  6. ebs r12 -- adadmin: error while loading shared libraries: libclntsh.so.10.1
  7. python基础:交互式解释器
  8. struts2:字段校验和非字段校验代码示例
  9. Windows Phone 三、样式和资源
  10. ubuntu配置tftp服务