[AngularJS面面观] 18. 依赖注入 --- $injector服务
有了前面那么多的铺垫工作, $injector服务首先我们看看这个服务中包含了那些方法: return {
invoke: invoke,instantiate: instantiate,get: getService,annotate: createInjector.$$annotate,has: function(name) {
return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
}
};
get以及has先挑软柿子捏,看看
function getService(serviceName,caller) {
if (cache.hasOwnProperty(serviceName)) {
if (cache[serviceName] === INSTANTIATING) {
throw $injectorMinErr('cdep','Circular dependency found: {0}',serviceName + ' <- ' + path.join(' <- '));
}
return cache[serviceName];
} else {
try {
path.unshift(serviceName);
cache[serviceName] = INSTANTIATING;
return cache[serviceName] = factory(serviceName,caller);
} catch (err) {
if (cache[serviceName] === INSTANTIATING) {
delete cache[serviceName];
}
throw err;
} finally {
path.shift();
}
}
}
这个方法其实在前面的文章中已经介绍过了,这里再来回顾一下加深印象: 所以
has: function(name) {
return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
}
判断一个对象是否存在,会去两个地方找: 关于第一点,我们暂且还不知道 annotate下面是获取注解信息的 function annotate(fn,strictDi,name) {
var $inject,argDecl,last;
if (typeof fn === 'function') {
if (!($inject = fn.$inject)) {
// 没有提供$inject并且非严格模式时,使用源码解析的方式构建$inject
$inject = [];
if (fn.length) {
if (strictDi) {
if (!isString(name) || !name) {
name = fn.name || anonFn(fn);
}
throw $injectorMinErr('strictdi','{0} is not using explicit annotation and cannot be invoked in strict mode',name);
}
argDecl = extractArgs(fn);
forEach(argDecl[1].split(FN_ARG_SPLIT),function(arg) {
arg.replace(FN_ARG,function(all,underscore,name) {
$inject.push(name);
});
});
}
fn.$inject = $inject;
}
} else if (isArray(fn)) {
// 当使用Array-Style的声明方式时,去掉最后一个元素即为$inject
last = fn.length - 1;
assertArgFn(fn[last],'fn');
$inject = fn.slice(0,last);
} else {
// 抛出异常
assertArgFn(fn,'fn',true);
}
// 得到注解信息供后续使用
return $inject;
}
关于注解信息,在前文中已经重点分析过了。它的主要作用是提供形式参数到真正被托管对象的一个关联。在angular中有三种提供这种关联关系的注解方式:
更多关于注解的分析,可以参考这篇文章:17. 依赖注入 — 注解的定义与实现 invoke
function invoke(fn,self,locals,serviceName) {
// 如果提供了locals并且它是字符串类型,则用它替换serviceName
if (typeof locals === 'string') {
serviceName = locals;
locals = null;
}
// 调用injectionArgs来完成真正参数的获取,实现在下面
var args = injectionArgs(fn,serviceName);
if (isArray(fn)) {
fn = fn[fn.length - 1];
}
// 判断fn是不是构造器函数,对待普通函数和构造器函数的处理方式不同(仅针对IE)
if (!isClass(fn)) {
// http://jsperf.com/angularjs-invoke-apply-vs-switch
// #5388
return fn.apply(self,args);
} else {
args.unshift(null);
return new (Function.prototype.bind.apply(fn,args))();
}
}
function injectionArgs(fn,serviceName) {
var args = [],// 获取注解信息
$inject = createInjector.$$annotate(fn,serviceName);
for (var i = 0,length = $inject.length; i < length; i++) {
var key = $inject[i];
// 确保$inject中的每个key都是字符串类型,否则抛出异常
if (typeof key !== 'string') {
throw $injectorMinErr('itkn','Incorrect injection token! Expected service name as string,got {0}',key);
}
// 通过注解的key来得到的真正依赖的对象 --- 如果locals中提供了拥有相同键值的对象,则优先使用它
args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
getService(key,serviceName));
}
return args;
}
// 从源码来看仅针对IE浏览器
function isClass(func) {
// IE 9-11 do not support classes and IE9 leaks with the code below.
if (msie <= 11) {
return false;
}
// Workaround for MS Edge.
// Check https://connect.microsoft.com/IE/Feedback/Details/2211653
return typeof func === 'function'
&& /^(?:classs|constructor()/.test(Function.prototype.toString.call(func));
}
上述代码不少,但是主线逻辑很明确:
protoInstanceInjector =
createInternalInjector(instanceCache,function(serviceName,caller) {
var provider = providerInjector.get(serviceName + providerSuffix,caller);
return instanceInjector.invoke(
provider.$get,provider,undefined,serviceName);
}),
上述代码看不懂没关系,等介绍了 instantiate了解了 function instantiate(Type,serviceName) {
// 当Type为数组时,根据基于数组风格的注解所规定的那样,数组中的最后一个元素才是函数
// 比如. someModule.factory('greeter',['$window',function(renamed$window) {}]);
var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
var args = injectionArgs(Type,serviceName);
// 处于第一个位置的空元素在使用new来调用函数的时候是必要的
args.unshift(null);
return new (Function.prototype.bind.apply(ctor,args))();
}
这两个方法是的区别主要在于调用传入 因此, 当然,还有好多细节我们现在还没法看懂,比如好多地方出现了 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- user-interface – 如何将持久数据结构“绑定”到Scala中的
- scala – 访问akka流的底层ActorRef Source.actorRef创建源
- Bootstrap CSS组件之导航条(navbar)
- angular – 使用TypeScript以编程方式检查Ionic 2单选按钮
- Scala Future map由于缺少ClassTag而无法编译
- Bash参数扩展分隔符
- WebService的实现之一jdk实现
- twitter-bootstrap – 集成bootstrap-tagsinput,boostrap3-
- twitter-bootstrap – Bootstrap:如何在网格设计中的任何位
- 为什么在scala中修改了if位置?