function invoke(fn, self, locals){
var args = [],
$inject = annotate(fn),
length, i,
key;
for(i = 0, length = $inject.length; i < length; i++) {
key = $inject[i];
if (typeof key !== 'string') {
throw $injectorMinErr('itkn',
'Incorrect injection token! Expected service name as string, got {0}', key);
}
args.push(
locals && locals.hasOwnProperty(key)
? locals[key]
: getService(key)
);
}
if (isArray(fn)) {
fn = fn[length];
}
// http://jsperf.com/angularjs-invoke-apply-vs-switch
// #5388
return fn.apply(self, args);
}
4. instantiate
用于实例化provider或者实例化服务的。
function instantiate(Type, locals) {
var Constructor = function() {},
instance, returnedValue;
// Check if Type is annotated and use just the given function at n-1 as parameter
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
instance = new Constructor();
returnedValue = invoke(Type, instance, locals);
return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
}
angularjs的一个很大的优势和特点就是依赖注入,下面我们分析一下angularjs源码中是怎么实现的。
在调用bootstrap的时候,调用createInjector来创建一个注射器进行注入。
createInjector这个方法的简单的代码如下
从上边的代码中,我们可以简略的看到,主要是定义了一些变量,然后调用createInternalInjector得到providerInjector,再次调用createInternalInjector得到instanceInjector。然后用loadModules依次加载模块,最后返回instanceInjector
下面介绍一下createInjector里面的几个变量
下面我们分析createInternalInjector。大致代码如下
从截图的代码可以看到createInternalInjector返回一个对象,对象上挂载了invoke,instantiate,get,annotate,has 这些方法。这里我们需要关注的是createInternalInjector被调用两次,这两次的返回值分别是providerInjector和instanceInjector。providerInjector这个内部注射器主要是用来处理provider的,instanceInjector主要是处理provider生成的单例的。
这个地方不太容易读懂,原因就在于createInternalInjector这个函数经过了抽象,把创建providerInjector和instanceInjector的过程抽象成一个函数。读起来容易混淆。这里需要好好去琢磨一下。
本段话如果弄不明白,先不要着急,你只需要先知道这个结论即可。随着咱们的对源码的慢慢剖析,这些最终都将比较容易理解。angular中的注射器有两种,一种是providerInjector,一种是instanceInjector。平时咱们一般写的都是instanceInjector。instanceInjector是负责管理providerInjector的实例的。刚刚咱们也分析了。providerInjector和instanceInjector上挂载的方法是一样的。但是作用却是大不相同。
下面我们具体分析一下这四种方法和其分别在两种注射器中的作用
1. get
由上面的截图中的代码可以看出,get其实就是getService。getService的详细代码如下
从上边的代码可以看到,providerInjector的get方法是尝试获取providerCache中缓存的provider,如果没有则执行工厂函数。从下面的截图可以看出,factory是一个会抛出异常的函数,也就是说当我们尝试去获取并不存在的provider的时候,会在控制台报错,提示 Unknown provider
instanceInjector的get方法是尝试获取instanceCache中缓存的provider创建的实例。如果没有,则执行穿进去的工厂函数创建一个。
2. annotate
该方法主要用于推断注入,我们知道angular中的依赖注入,可以用推断式和行内注入,推断式注入主要是通过调用函数的toString方法,得到函数的签名,用正则分析出函数的形参。从而得到依赖。 行内注入主要是通过截取数组的0到数组长度减1获取到依赖。
3. invoke
执行传入的函数(数组函数),比如
invoke此时执行的是就是fn函数,先是分析fn的依赖,然后去拿依赖,如果没有相关的依赖,会先创建相关的依赖。然后执行fn
这里有个有点绕的问题再次跑出来。大家可以想象一下,执行providerInjector.invoke 和instanceInjector.invoke的区别是什么?
也可以看一下下面这个问题产生的根源是什么?demo我写在了codepen中。 https://codepen.io/zhaojianxin/pen/BGxmaa
为什么$provide在config中调用就没有问题,在controller中调用就会报错?
4. instantiate
用于实例化provider或者实例化服务的。