[AngularJS面面观] 19. 依赖注入 --- Provider是个啥
在前面介绍angular中依赖注入相关的概念和细节时,非常多次提到了 provider是什么?通过对象声明provider首先,我们来看看 比如下面的对象就是符合要求的 {
$get: function() {
return 'aConstant';
}
}
上面的对象定义了一个 现在,我们定义了一个最简单的 var app = angular.module('test',[]);
// 通过provider提供一个常量的获取方法
app.provider('a',{
$get: function() {
return 'aConstant';
}
});
// 直接通过constant定义一个常量
app.constant('b','bConstant');
在上述代码中,我们分别使用 现在我们回头看看 provider: invokeLaterAndSetModuleName('$provide','provider')
// invokeLaterAndSetModuleName本身也是一个函数,返回另一个函数
function invokeLaterAndSetModuleName(provider,method) {
return function(recipeName,factoryFunction) {
if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
invokeQueue.push([provider,method,arguments]);
return moduleInstance;
};
}
});
当然,为了使 var app = angular.module('test',{
// 声明依赖关系,最终得到的a常量为"aConstantbConstant"
$get: function(b) {
return 'aConstant' + b;
}
});
// 直接通过constant定义一个常量
app.constant('b','bConstant');
函数的柯里化看上去很奇怪,一个函数返回了另外一个函数。其实这里涉及到一个叫做”函数的柯里化“的概念,是一种常见的将接受多个参数的函数转化为只接受一个或者有限几个的函数的方法。为了分析这个问题,我们不妨假设一下现在并没有 function(provider,recipeName,factoryFunction) {}
四个参数有点多了,而且复用性不佳。使用了柯里化将四个参数的函数转换为两个参数的函数,同时还能够在函数内部完成一些额外的任务,比如在 angular.module('test',[]).constant('a','aConstant').constant('b','bConstant');
定义了 function runInvokeQueue(queue) {
var i,ii;
for (i = 0,ii = queue.length; i < ii; i++) {
var invokeArgs = queue[i],// 得到$provide对象
provider = providerInjector.get(invokeArgs[0]);
// invokeArgs的对应关系:[0] - '$provide',[1] - 'provider',[2] - [providerName,providerDef]
provider[invokeArgs[1]].apply(provider,invokeArgs[2]);
}
}
执行任务队列的关键就在于上述for循环中的最后一行: function provider(name,provider_) {
// 确保name不等于hasOwnProperty
assertNotHasOwnProperty(name,'service');
// 如果提供的是provider_是函数或者数组,直接使用injector实例化
if (isFunction(provider_) || isArray(provider_)) {
provider_ = providerInjector.instantiate(provider_);
}
// 如果provider_没有提供$get方法,抛出异常
if (!provider_.$get) {
throw $injectorMinErr('pget',"Provider '{0}' must define $get factory method.",name);
}
// 将provider_放入到providerCache中
return providerCache[name + providerSuffix] = provider_;
}
可以发现,最终 通过构造器函数声明provider那么,从代码中可以看出 var app = angular.module('test',[]);
// 通过provider提供一个常量的获取方法,使用构造器函数
app.provider('a',function AProvider() {
this.$get = function() {
return 'aConstant';
};
});
函数的名字并非一定要是 所以通过构造器函数的方式来定义 var app = angular.module('test',[]);
app.constant('b','bConstant');
// 通过provider提供一个常量的获取方法,使用构造器函数并声明依赖
app.provider('a',function AProvider(b) {
this.$get = function() {
// 那么最终a常量的定义就是: aConstantbConstant
return 'aConstant' + b;
};
});
两种provider声明方式的区别现在,我们目前有两种方式来定义 那么这两种方式是否是一样的,在任何场合下都能够互换呢?答案是否定的,其实答案在上述介绍 function provider(name,name);
}
// 将provider_放入到providerCache中
return providerCache[name + providerSuffix] = provider_;
}
区别就在于懒加载是否能够实现。 第一种使用对象的 那么是谁来实例化这个我们定义的构造器函数形式的 function createInjector(modulesToLoad,strictDi) {
strictDi = (strictDi === true);
var INSTANTIATING = {},providerSuffix = 'Provider',path = [],loadedModules = new HashMap([],true),// provider cache以及provider injector
providerCache = {
// ......
},providerInjector = (providerCache.$injector =
createInternalInjector(providerCache,function(serviceName,caller) {
if (angular.isString(caller)) {
path.push(caller);
}
throw $injectorMinErr('unpr',"Unknown provider: {0}",path.join(' <- '));
})),// instance cache以及instance injector
instanceCache = {},protoInstanceInjector =
createInternalInjector(instanceCache,caller) {
var provider = providerInjector.get(serviceName + providerSuffix,caller);
return instanceInjector.invoke(
provider.$get,provider,undefined,serviceName);
}),instanceInjector = protoInstanceInjector;
// ......
// 返回的是instance injector
return instanceInjector;
// 其它内部函数...
}
答案很明确,并不是一个东西。返回的是 真是一个问题接着一个问题,解决了一个问题又引出了另外一个问题。不过这也正是程序开发的有意思之处。世界这么复杂,怎么可能三言两语就讲的明白呢。 那么我们的问题就转变成了为何要如此设计呢, (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |