Injector 注入器 - acelan86/angular GitHub Wiki
该方法用来创建注入器,注入的概念请参照angularjs官网说明
loadModules 加载模块
loadModules在创建注入器时被调用,返回runBlocks数组
var loadedModules = new HashMap(); //用来记录加载过的模块
...
/**
* 改方法完成如下事情:
* 1. 遍历所有需要加载的模块modulesToLoad
* 2. 如果需要加载的module是一个字符串,执行下面的步骤:
* 2.1 调用angular.module方法获取或新建一个模块moduleInatance
* 2.2 执行loadModules(moduleInstance.requires), 将moduleInstace._runBlocks和执行结果concat到runBlocks结果集中
* 2.3 执行moduleInatance._invokeQueue中记录的所有方法
* 否则,xxxx
* 否则,xxxx
* 3. 将runBlocks返回
*/
function loadModules(modulesToLoad){
var runBlocks = [];
forEach(modulesToLoad, function(module) { //#1
if (loadedModules.get(module)) return;
loadedModules.put(module, true);
if (isString(module)) { //#2
var moduleFn = angularModule(module); //#2.1
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); #2.2
try { //#2.3
for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
var invokeArgs = invokeQueue[i],
provider = invokeArgs[0] == '$injector'
? providerInjector
: providerInjector.get(invokeArgs[0]);
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
}
} catch (e) {
if (e.message) e.message += ' from ' + module;
throw e;
}
} else if (isFunction(module)) { //else
try {
runBlocks.push(providerInjector.invoke(module));
} catch (e) {
if (e.message) e.message += ' from ' + module;
throw e;
}
} else if (isArray(module)) { //else
try {
runBlocks.push(providerInjector.invoke(module));
} catch (e) {
if (e.message) e.message += ' from ' + String(module[module.length - 1]);
throw e;
}
} else {
assertArgFn(module, 'module');
}
});
return runBlocks; //#3
}
createInjector创建注入器
function createInjector(modulesToLoad) {
var loadedModules = new HashMap(), //就是一个obj模拟hasMap, 记录已经加载过的模块
providerCache = {
$provide: { ... }
},
providerInjector = createInternalInjector(provideCache,...), //先不管他们是啥
instanceCache = {},
instanceInjector = instanceCache.$injector = createInternalInjector(instanceCache, ...); //先不管他们是啥
...
forEach(
loadModules(modulesToLoad),
function(fn) {
instanceInjector.invoke(fn || noop);
}
);
...
return instanceInjector;
}
function createInternalInjector(cache, factory) {
function getService(serviceName) {
//依赖是一个服务,获取该服务
//这里会用到cache和factory
//这里做的就是,如果是一个service,那么从cache中(可能是一个providerCache,也可能是一个injectorCache)中获取name为serviceName的service,返回
}
//依赖注入的过程
function invoke(fn, self, locals){
var args = [],
$inject = annotate(fn), //将fn中的依赖注入模块名解析出来,返回$inject, annotate见下面说明
length, i,
key;
//得到$inject中需要依赖注入的实际值(可能是locals或者是一个service)存放在args中,作为调用实参
for(i = 0, length = $inject.length; i < length; i++) {
key = $inject[i];
args.push(
locals && locals.hasOwnProperty(key)
? locals[key]
: getService(key)
);
}
//找不到fn.$inject说明传入的fn是一个['$xxx', function ($xxx) {}]这样的依赖声明格式
if (!fn.$inject) {
fn = fn[length]; //最后一个元素为真正要执行的fn
}
//源码中一坨switch简单看成:
return fn.apply(self, args); //这里就是真正的依赖注入,将依赖与执行函数的形参绑定,完成注入
}
//这方法还没细看干嘛的,感觉就是invoke的一个包装而已。
function instantiate(Type, locals) {
...
return isObject(returnedValue) ? returnedValue : instance;
}
return {
invoke: invoke,
instantiate: instantiate,
get: getService,
annotate: annotate
};
}
annotate注解方法
此方法用来解析依赖注入的不同写法, 最终返回注入的依赖名称
//方法1
function foo($scope, $service) {
...todo something
}
//方法2, function的形参可以随意名字
['$scope', '$sercice', function ($scope, $service) {
..todo something;
}]
//方法3
function foo($scope, $service) {
..todo something;
}
foo.$inject = ['$scope', '$service'];
实现如下:
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; //匹配function (xxx, xxx)中的'xxx,xxx'这样的依赖注入名称字符串
var FN_ARG_SPLIT = /,/; //你懂的
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; //匹配参数名
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; //匹配代码中的注释
function annotate(fn) {
var $inject,
fnText,
argDecl,
last;
if (typeof fn == 'function') {
if (!($inject = fn.$inject)) {
$inject = [];
fnText = fn.toString().replace(STRIP_COMMENTS, ''); //去除注释
argDecl = fnText.match(FN_ARGS);
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
arg.replace(FN_ARG, function(all, underscore, name){
$inject.push(name);
});
});
fn.$inject = $inject; //这里对没有$inject属性的fn进行赋值,保证一个function类型的fn一定有保持依赖名字的属性
}
} else if (isArray(fn)) {
last = fn.length - 1;
assertArgFn(fn[last], 'fn');
$inject = fn.slice(0, last);
} else {
assertArgFn(fn, 'fn', true);
}
return $inject;
}