[AngularJS面面观] 15. 依赖注入 --- 初识注入器(Injector)
本篇文章继续介绍angular中实现依赖注入的”幕后英雄” — 注入器(Injector)。说它是”幕后英雄”,是因为它才是依赖注入得以实现的主力。而上篇文章介绍的模块只不过是活跃在前台跟各位开发人员直接打交道的”接待人员”。 初识注入器加载模块在上一篇文章中,介绍了angular是如何定义和使用模块的。在这些模块中定义了各种应用需要的服务,注意模块只是定义了服务,而并没有真正地创建它们。真正的创建是通过注入器来完成的,那么怎么注入器是怎么知道需要创建哪些服务的呢? 我们都知道angular中可以定义常量,而定义的方法也很简单: var app = angular.module('test',[]);
app.constant('testConstant','This is a constant value');
这里调用了 那么注入器是什么时候才会介入进来呢?答案就是,当模块被加载到注入器的时候,注入器就会知道被加载的模块中定义了哪些任务,从而介入到这个创建相应服务的任务中来了。 这也是创建注入器的入口函数中 function createInjector(modulesToLoad) {
// HashMap是定义在apis.js中的一个类型,用来将数组转换为key具有一定生成规则的字典对象
var loadedModules = new HashMap([],true);
// 加载模块
var runBlocks = loadModules(modulesToLoad);
// 执行真正的创建工作
forEach(runBlocks,function(fn) { if (fn) instanceInjector.invoke(fn); });
}
上面的 上述代码中重要的逻辑是模块在被加载后会发生什么,它由 function loadModules(modulesToLoad) {
var runBlocks = [],moduleFn;
// 依次加载定义在依赖数组中的每一个module
forEach(modulesToLoad,function(module) {
// 如果已经加载过参数指定的module,立刻返回
if (loadedModules.get(module)) return;
loadedModules.put(module,true);
if (isString(module)) {
// 如果是以字符串形式定义的module --- 即module的名字
moduleFn = angularModule(module);
// 递归进行module的加载工作,因为被依赖的module同样还可以依赖于别的module(这里出现的runBlocks我们先行忽略,重点在其中loadModules函数的再次调用)
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
runInvokeQueue(moduleFn._invokeQueue);
} else if (isFunction(module)) {
// 如果是以函数形式定义的module
} else if (isArray(module)) {
// 如果是以数组形式定义的module
} else {
// 当module不符合上述几种类型时,抛出异常
assertArgFn(module,'module');
}
});
return runBlocks;
}
有几个地方值得注意: 另外,上述方法中还用到了两个内部函数: 下面我们就来具体看看任务队列究竟是啥。 任务队列前面我们提到过在模块中,即使你定义了一个constant,这个constant也不会立马被创建出来。创建的只是一个等待注入器来执行的任务,所以就定义constant而言,看看具体的实现(由于实现在module中,因此代码在loader.js中): // 一个module的实例包含的字段:现在又来了两个概念:_invokeQueue(任务队列)以及constant方法
var moduleInstance = {
_invokeQueue: invokeQueue,// invokeQueue会被初始化为一个空数组
requires: requires,name: name,constant: invokeLater('$provide','constant','unshift')
}
// invokeLater函数的实现
function invokeLater(provider,method,insertMethod,queue) {
if (!queue) queue = invokeQueue;
return function() {
queue[insertMethod || 'push']([provider,arguments]);
return moduleInstance;
};
}
constant被定义为调用 做一些简单参数替换工作,执行的代码如下: invokeQueue['unshift'](['$provide','testConstant','This is a constant value']);
return moduleInstance;
也就是把数组 那么这个 if (isString(module)) {
// 如果是以字符串形式定义的module --- 即module的名字
moduleFn = angularModule(module);
// 开始执行任务队列
runInvokeQueue(moduleFn._invokeQueue);
}
对,就是这个 它的实现如下所示: 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]);
}
}
这段代码会挨个地从任务队列中拿出数据,然后执行。执行的主体是一个叫做 怎么样,以上就是注入器中关于模块加载以及任务执行的部分实现。新出现的概念比较多,如果有不明白的地方请慢慢回味。如果对模块这个概念也不熟悉的话,可以回顾上一篇文章中关于模块的介绍。 在下一篇文章中,我们会继续探讨注入器是如何真正实现依赖注入的。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |