[AngularJS面面观] 21. 依赖注入 --- constant到底如何而来
在上一篇文章中,我们终于见到了angular中依赖注入的总体结构图。从这幅图中我们可以知道在angular内部是有两个注入器协同工作来实现我们习以为常的依赖注入特性的。 结合上图简单回顾一下angular依赖注入的组成和工作流程。 首先,在台面上的注入器名为实例注入器(Instance Injector),它里面含有一个名为实例缓存(Instance Cache)的字典对象,该缓存的作用是保存被托管的对象,每个被注入器实例化得到的对象都会被保存在其中。所谓的依赖注入,实际上就是从实例注入器的缓存中拿去我们需要的对象。当然,凡事都有第一次,当我们需要的对象并不在该缓存中时,也就是说该对象还没有被实例化。那么这个时候实例注入器就要像provider注入器求援了。因为在这个provider注入器中保存的都是用于实例化对象的”菜谱”,而这些”菜谱”就定义在了每个 基于provider的高层API然而,我们在真正地应用angular框架来完成我们的业务逻辑时,直接使用 constant的一生按照惯例,还是先挑软柿子捏,最简单的服务非 定义我们都知道要声明 constant: invokeLater('$provide','constant','unshift')
function invokeLater(provider,method,insertMethod,queue) {
if (!queue) queue = invokeQueue;
// 还是利用柯里化将多个参数的函数转换为少数参数的函数
return function() {
// arguments才是我们在声明constant时实际传入的参数
queue[insertMethod || 'push']([provider,arguments]);
return moduleInstance;
};
}
// 将上面代码还原一下,constant的实际定义是这样的:
constant: function() {
invokeQueue['unshift'](['$provide',arguments]);
return moduleInstance;
}
这里仍然使用了JavaScript中一个被经常使用的模式,即函数的”柯里化”,它的主要作用是减少函数的参数数量,是一个函数式编程中经常会被使用到的模式。 所以对于 那么我们来看看当执行任务队列的时候会发生什么,这段代码我们已经提过很多次了,算是注入器实现中的核心代码: // 执行模块中的任务队列
runInvokeQueue(moduleFn._invokeQueue);
// 对照定义constant的参数:(['$provide','constant',arguments])
// invokeArgs[0]: '$provide'
// invokeArgs[1]: 'constant':
// invokeArgs[2]: 类数组对象arguments
function runInvokeQueue(queue) {
var i,ii;
// 以此从任务队列中拿到任务,然后拿到对应的provider并进行调用
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]);
}
}
将定义 // 假设我们定义了一个constant如下所示:
module.constant('a','aConstant');
// 代入到任务执行阶段:
var invokeArgs = ['$provide',['a','aConstant']],provider = providerInjector.get('$provide');
provider['constant'].apply(provider,'aConstant']);
注意其中的 因此从这里我们就可以很明确地发现,负责提供 // $provide直接被定义到了provider注入器的缓存中
providerCache = {
$provide: {
constant: supportObject(constant),// ......
}
}
function supportObject(delegate) {
return function(key,value) {
if (isObject(key)) {
forEach(key,reverseParams(delegate));
} else {
return delegate(key,value);
}
};
}
function constant(name,value) {
// 确保常量的名字不叫做'hasOwnProperty'
assertNotHasOwnProperty(name,'constant');
// 将常量直接定义到provider注入器和instance注入器的缓存中
providerCache[name] = value;
instanceCache[name] = value;
}
function assertNotHasOwnProperty(name,context) {
if (name === 'hasOwnProperty') {
// 抛出badname异常
throw ngMinErr('badname',"hasOwnProperty is not a valid {0} name",context);
}
}
可以发现,
第二行和第三行做的事情就更简单直白了。将 function provider(name,provider_) {
assertNotHasOwnProperty(name,'service');
if (isFunction(provider_) || isArray(provider_)) {
// provider的实例化由provider注入器完成,并非由instance注入器完成
provider_ = providerInjector.instantiate(provider_);
}
if (!provider_.$get) {
throw $injectorMinErr('pget',"Provider '{0}' must define $get factory method.",name);
}
return providerCache[name + providerSuffix] = provider_;
}
从代码中可以得知, 所以, // 在provider的构造器函数中直接声明常量依赖
module.provider('b',function BProvider(a) {
this.$get = function() {
return 'constant: ' + a;
};
});
// 在service中声明常量依赖
module.service('aService',function(a) {
// ......
});
// 定义在最后也没关系:别忘了常量任务会通过unshift操作放到任务队列的头部
module.constant('a','aConstant');
通过上面的分析,想必对依赖注入和angular注入器内部的实现方式有更深入的了解了吧。在后续的文章中,会继续分析定义在module中的各种我们在开发angular应用时经常会使用到的方法。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |