angular源码分析:injector.js文件分析——angular中的依赖注入式如何实现的(续)
昨天晚上写完angular源码分析:angular中jqLite的实现——你可以丢掉jQuery了,给今天定了一个题angular源码分析:injector.js文件,以及angular的加载流程,但是想了想,加载流程还是放到后面统一再讲比较好。
如果你没有看过笔者的angular源码分析:angular中的依赖注入式如何实现的,可以点击看看,在其中讲过的内容,我将不会再这里重复,这一期将作那一期的补充。
一、从createInjector函数开始
先省去具体实现,总体看看:函数拥有两个参数,modulesToLoad, strictDi,从单词命名上来看,第一个参数是要没加载的模块,第二参数是严格的依赖注入;另外函数对象本身绑定了一个annotate,在之前我们讲过annotate是一个可以将函数中的参数提出来的函数。
function createInjector(modulesToLoad, strictDi) { ...}
createInjector.$$annotate = annotate;
二、理解createInjector函数的实现
先上代码:
strictDi = (strictDi === true);
var INSTANTIATING = {},
providerSuffix = 'Provider',
path = [],
loadedModules = new HashMap([], true),
providerCache = { //用存放provider的cache
$provide: {
provider: supportObject(provider),
factory: supportObject(factory),
service: supportObject(service),
value: supportObject(value),
constant: supportObject(constant),
decorator: decorator
}
},
providerInjector = (providerCache.$injector =
createInternalInjector(providerCache, function(serviceName, caller) { //调用createInternalInjector生成内部的注入器
if (angular.isString(caller)) {
path.push(caller);
}
throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
})),
instanceCache = {}, //用来存放服务实例的cache
instanceInjector = (instanceCache.$injector =
createInternalInjector(instanceCache, function(serviceName, caller) {
var provider = providerInjector.get(serviceName + providerSuffix, caller);
return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
}));
forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
return instanceInjector;
1.这个函数做了什么。
基本上是定义一堆东西,其中最重要的是providerCache和providerInjector 以及instanceCache和instanceInjector。关于createInternalInjector这个函数,在angular源码分析:angular中的依赖注入式如何实现的中讲过,主要功能是利用提供的cache(第一个参数u)和factory(第二参数),构造一个内部注入器,其本身也是工厂函数。
2.providerCache 和$provide
我们可以这样理解,providerCache中存放的就是各种服务的提供者的实例。比如定义一个服务,叫"dapeng",那么提供者就是"dapengProvider"。而这个容器(providerCache)在初始化时,就默认放入了一个$provide。
providerCache = {
$provide: {
provider: supportObject(provider),
factory: supportObject(factory),
service: supportObject(service),
value: supportObject(value),
constant: supportObject(constant),
decorator: decorator
}
},
很眼熟吧,这是不是可以用于定义服务的几种方式呢?
3.forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });中的loadModules函数:
////////////////////////////////////
// Module Loading
////////////////////////////////////
function loadModules(modulesToLoad) {
assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
var runBlocks = [], moduleFn;
forEach(modulesToLoad, function(module) {
if (loadedModules.get(module)) return;
loadedModules.put(module, true);
function runInvokeQueue(queue) { //执行调用队列
var i, ii;
for (i = 0, ii = queue.length; i < ii; i++) {
var invokeArgs = queue[i],
provider = providerInjector.get(invokeArgs[0]);
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
}
}
try {
if (isString(module)) {
moduleFn = angularModule(module); //加载模块
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); //递归加载依赖模块,获取所有模块的run函数定义的代码。
runInvokeQueue(moduleFn._invokeQueue); //moduleFn._invokeQueue是什么鬼,先留坑在此
runInvokeQueue(moduleFn._configBlocks); //moduleFn._configBlocks是什么鬼,也留坑在此
} else if (isFunction(module)) {
runBlocks.push(providerInjector.invoke(module));
} else if (isArray(module)) {
runBlocks.push(providerInjector.invoke(module));
} else {
assertArgFn(module, 'module');
}
} catch (e) {
if (isArray(module)) {
module = module[module.length - 1];
}
if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
// Safari & FF's stack traces don't contain error.message content
// unlike those of Chrome and IE
// So if stack doesn't contain message, we create a new string that contains both.
// Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
/* jshint -W022 */
e = e.message + '\n' + e.stack;
}
throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
module, e.stack || e.message || e);
}
});
return runBlocks;
}
angularModule是什么鬼,留坑先不讲。可以先理解为,用于加载一个module。
runBlocks将得到一个数组,这个数组的元素是一些函数,这些函数是定义模块后通过run(function(){})注册的函数。
runInvokeQueue函数,将执行调用队列,可以从这个函数的实现上来看,参数queue应该是一个二维数组。[['name','index',params]],这个函数将循环处理queue数组。
** moduleFn._invokeQueue 和 moduleFn._configBlocks** 本期先不讲,留坑在此,等讲“加载流程”再讲。
那么,这个函数最红就是返回的一个函数数组:runBlocks。
可以基本推出:forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });是执行所有的模块中run的代码,而在run的代码执行前,先执行了服务的定义代码和模块config代码。
三、$provider
还是先上代码:
////////////////////////////////////
// $provider
////////////////////////////////////
function supportObject(delegate) {
return function(key, value) {
if (isObject(key)) { //如果key是一个对象
forEach(key, reverseParams(delegate));
} else {
return delegate(key, value);
}
};
}
function provider(name, provider_) {
assertNotHasOwnProperty(name, 'service');
if (isFunction(provider_) || isArray(provider_)) {
provider_ = providerInjector.instantiate(provider_);
}
if (!provider_.$get) {
throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
}
return providerCache[name + providerSuffix] = provider_;
}
function enforceReturnValue(name, factory) {
return function enforcedReturnValue() {
var result = instanceInjector.invoke(factory, this);
if (isUndefined(result)) {
throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
}
return result;
};
}
function factory(name, factoryFn, enforce) {
return provider(name, {
$get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
});
}
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
function value(name, val) { return factory(name, valueFn(val), false); }
function constant(name, value) {
assertNotHasOwnProperty(name, 'constant');
providerCache[name] = value;
instanceCache[name] = value;
}
function decorator(serviceName, decorFn) {
var origProvider = providerInjector.get(serviceName + providerSuffix),
orig$get = origProvider.$get;
origProvider.$get = function() {
var origInstance = instanceInjector.invoke(orig$get, origProvider);
return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
};
}
1.supportObject函数
这里先引用一下reverseParams的实现
function reverseParams(iteratorFn) {
return function(value, key) { iteratorFn(key, value); };
}
来看看下面的代码将会得到什么,就知道这个函数在做什么了:
function agent(key,vlue){
console.log(key + '--->'+ value);
}
var new_agent = supportObject(agent);
new_agent({a:123,b:456,c:'abc'});
new_agent('key','value');
2.provider函数,参数name,provider_
作用,创建服务的提供者,serviceProvider,并且用providerCache保存起来。
3.enforceReturnValue
直接调用instanceInjector.invoke来生成服务。
4.factory
调用provide函数,由函数自身提供一个Provider。
5.service,继续简化服务的定义。
这意味者,可以给service的第二参数传递一个构成函数,service会利用构造函数“new”出一个服务对象来。如果你已经有一个构造函数,需要定义这个构造函数生成的对象为服务,可以考虑使用这个方法。
6.value,继续简化服务的定义。
第二个参数,是一个对象(可以是一个基础类型的值)或者是一个可以返回对象的函数。如果你定义的服务是一个对象,可以考虑用这个方法。
7.constant
可以看到providerCache和instanceCache中的存储用的键是一个,就是说,通过这个函数定义的服务,可以在模块的config阶段和run阶段同时有效。
8.decorator,装饰模式的实现
通过代码分析,可以得到结论:decorator可以对一个已有的服务进行重新装饰。
举例
decorator('exist_service',function($delegate){
$delegate.add_method = function(){
console.log('this method is added');
};
});
上面的代码,将会向服务“exist_service”增加一个add_method方法。
在最新的angular版本中,已经可以采用angular.module('xxx').decorator('exist_service',function($delegate){})的方式来使用decorator,我们讲的这个版本,还只能在config中使用。
上一期:angular源码分析:angular中jqLite的实现——你可以丢掉jQuery了
下一期:angular源码分析:angular的整个加载流程
angular源码分析:injector.js文件分析——angular中的依赖注入式如何实现的(续)的更多相关文章
- zepto源码研究 - ajax.js(请求过程中的各个事件分析)
简要:ajax请求具有能够触发各类事件的功能,包括:触发全局事件,请求发送前事件,请求开始事件,请求结束事件等等,贯穿整个ajax请求过程,这是非常有用的,我们可以利用这些事件来做一些非常有意思的事情 ...
- angular源码分析:angular的整个加载流程
在前面,我们讲了angular的目录结构.JQLite以及依赖注入的实现,在这一期中我们将重点分析angular的整个框架的加载流程. 一.从源代码的编译顺序开始 下面是我们在目录结构哪一期理出的an ...
- angular源码分析:angular中jqLite的实现——你可以丢掉jQuery了
一.从function JQLite(element)函数开始. function JQLite(element) { if (element instanceof JQLite) { //情况1 r ...
- angular源码分析:angular的源代码目录结构说明
一.读源码,是选择"编译合并后"的呢还是"编译前的"呢? 有朋友说,读angular源码,直接看编译后的,多好,不用管模块间的关系,从上往下读就好了.但是在我看 ...
- angular源码分析:angular中各种常用函数,比较省代码的各种小技巧
angular的工具函数 在angular的API文档中,在最前面就是讲的就是angular的工具函数,下面列出来 angular.bind //用户将函数和对象绑定在一起,返回一个新的函数 angu ...
- angular源码分析:angular中入境检察官$sce
一.ng-bing-html指令问题 需求:我需要将一个变量$scope.x = '<a href="http://www.cnblogs.com/web2-developer/&qu ...
- angular源码分析:angular中脏活累活承担者之$parse
我们在上一期中讲 $rootscope时,看到$rootscope是依赖$prase,其实不止是$rootscope,翻看angular的源码随便翻翻就可以发现很多地方是依赖于$parse的.而$pa ...
- angular源码分析:angular中$rootscope的实现——scope的一生
在angular中,$scope是一个关键的服务,可以被注入到controller中,注入其他服务却只能是$rootscope.scope是一个概念,是一个类,而$rootscope和被注入到cont ...
- angular源码分析:angular中的依赖注入式如何实现的
一.准备 angular的源码一份,我这里使用的是v1.4.7.源码的获取,请参考我另一篇博文:angular源码分析:angular源代码的获取与编译环境安装 二.什么是依赖注入 据我所知,依赖注入 ...
随机推荐
- Lua 学习笔记(二)语法、类型、值
首先Lua执行的每一段代码都称之为“程序块”,一个程序块也就是一连串的语句或命令,例如一个源码文件或一行代码.Lua语句之间并不需要分隔符,如代码中的换行就不起任何作用,当然为了养成编码习惯当两条或者 ...
- JavaScript的学习--JavaScript设计模式的总结
这篇博客只是自己对设计模式的理解的备忘~ 看完了<JavaScript设计模式>这本书,一直没有写博客记录一下,最近抽出时间来重读了一下,就顺便记录一下~ 如果你只是想粗略了解一下Java ...
- Hadoop入门学习笔记---part1
随着毕业设计的进行,大学四年正式进入尾声.任你玩四年的大学的最后一次作业最后在激烈的选题中尘埃落定.无论选择了怎样的选题,无论最后的结果是怎样的,对于大学里面的这最后一份作业,也希望自己能够尽心尽力, ...
- 聊Javascript中的AOP编程
Duck punch 我们先不谈AOP编程,先从duck punch编程谈起. 如果你去wikipedia中查找duck punch,你查阅到的应该是monkey patch这个词条.根据解释,Mon ...
- TextView显示html文件中的图片
fromHtml还有另一个重构:fromHtml(String source, Html.ImageGetter imageGetter, Html.TagHandler tagHandler) 而I ...
- html/css基础篇——iframe和frame的区别【转】
转自共享圈的使用iframe的优缺点,为什么少用iframe以及iframe和frame的区别.其中本人不认同的地方有做小修改 注:HTML5不再支持使用frame,iframe只有src 属性 一. ...
- 解决AndroidStudio升级版本后恢复初始化设置的问题
今天把AndroidStudio升级到1.5后发现所有的个性设置全变为初始化了.包括皮肤啊,字体大小.颜色啊,以及快捷键等等.一瞬间就懵了. 升级完后好像有一个弹窗就是提示是否要继续使用之前的配置的, ...
- BZOJ1179 Atm //缩点+spfa
1179: [Apio2009]Atm Description Input 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的 ...
- Ionic2学习笔记(3):Pipe
作者:Grey 原文地址: http://www.cnblogs.com/greyzeng/p/5538630.html Pipe类似过滤器,比如,在一个字符串要展现在页面之前, 我们需要对这个字符串 ...
- SqlCommandBuilder实现大数据更新
/// <param name="table">准备更新的DataTable新数据</param> /// <param name="Tab ...