大佬教程收集整理的这篇文章主要介绍了30行代码让你理解angular依赖注入:angular 依赖注入原理,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
依赖注入(Dependency Injection,简称DI)是像C#,java等典型的面向对象语言框架设计原则控制反转的一种典型的一种实现方式,angular把它引入到js中,介绍angular依赖注入的使用方式的文章很多,
angular官方的文档,也有很详细的说明。但介绍原理的较少,angular代码结构较复杂,文章实现了一简化版本的DI,核心代码只有30行左右,相看实现效果(可能需FQ)或查看源码
这篇文章用尽量简单的方式说一说 angular依赖注入的实现。
要实现注入,基本有三步:
javascript 实现DI的核心api是Function.prototype.toString
,对一个函数执行toString,它会返回函数的源码字符串,这样我们就可以通过正则匹配的方式拿到这个函数的参数列表:
function extractArgs(fn) { //angular 这里还加了注释、箭头函数的处理
var args = fn.toString().@H_269_40@match(/^[^\(]*\(\s*([^\)]*)\)/m);
return args[1].split(',');
}
java与.net通过反射来获取依赖对象,js是动态语言,直接一个object[name]
就可以直接拿到对象。所以只要用一个对象保存对象或函数列表就可以了
createInjector(cache) {
this.cache = cache;
}
angular.@H_269_40@module = function () {
modules = {};
injector = new createInjector(modules);
return {
injector: injector,
factory: function (name, fn) {
modules[name.trim()] = this.injector.invoke(fn);
return this;
}
}
};
最后通过 fn.apply方法把执行上下文,和依赖列表传入函数并执行:
createInjector.prototype = {
invoke: function (fn, self) {
argsString = extractArgs(fn);
args = [];
argsString.forEach(function (val) {
args.push(this.cache[val.trim()]);
}, this);
return fn.apply(self, args);
}
};
简化的全部代码和执行效果见(可能需FQ):http://plnkr.co/edit/sJiIbzEXiqLLoQPeXBnR?p=preview
或查看源码
这里是简化的版本,实际angular的实现考虑了很多问题,如模块管理,延迟执行等
为了简单,我们也按这三步来介绍angular DI
注:以下代码行数有就可能变
https://github.com/angular/angular.js/blob/master/src%2Fauto%2Finjector.js#L81
var ARROW_ARG = /^([^\(]+?)=>/;
var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
var StriP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
extractArgs(fn) {
var fntext = fn.toString().replace(StriP_COMMENTS, ''),
args = fntext.@H_269_40@match(ARROW_ARG) || fntext.@H_269_40@match(FN_ARGS);
return args;
}
https://github.com/angular/angular.js/blob/master/src%2Fauto%2Finjector.js#L807
getservice(servicename, caller) {
if (cache.hasOwnProperty(serviceName)) {
if (cache[servicename] === INSTANTIATinG) {
throw $injectorminerr('cdep',21)">'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();
}
}
}
https://github.com/angular/angular.js/blob/master/src%2Fauto%2Finjector.js#L831
injectionArgs(fn, locals, servicename) {
var args = [],
$inject = createInjector.$$Annotate(fn, StrictDi, serviceName);
for (var i = 0, length = $inject.length; i < length; i++) {
var key = $inject[i];
if (typeof key !== 'String') {
'itkn',
'Incorrect injection token! Expected service name as String,got {0}', key);
}
args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
getservice(key, serviceName));
}
return args;
}
https://github.com/angular/angular.js/blob/master/src%2Fauto%2Finjector.js#L861
invoke(fn, self, servicename) {
typeof locals === 'String') {
servicename = locals;
locals = null;
}
var args = injectionArgs(fn, locals, serviceName);
if (isArray(fn)) {
fn = fn[fn.length - 1];
}
if (!isClass(fn)) {
// http://jsperf.com/angularjs-invoke-apply-vs-switch
// #5388
:rgb(0, args);
} else {
args.unshift(null);
new (Function.prototype.bind.apply(fn, args))();
}
}
angular在每次应用启动时,初始化一个Injector实例:
https://github.com/angular/angular.js/blob/master/src/Angular.js#L1685
由此代码可以看出对每一个Angular应用来说,无论是哪个模块,所有的"provider"都是存在相同的providerCache或cache中
所以会导致一个被誉为angular模块管理的坑王的问题:
module 并没有什么命名空间的作用,当依赖名相同的时候,后面引用的会覆盖前面引用的模块。
具体的示例可以查看:
http://plnkr.co/edit/TZ7hpMwuxk0surlcWDvU?p=preview
@H_607_971@注:angular di用本文的调用方式压缩代码会出问题:可以用g-annotate转为安全的调用方式。
到此angular di的实现原理已完成简单的介绍,angular用了项目中几乎不会用到的api:Function.prototype.toString 实现依赖注入,思路比较简单,但实际框架中考虑的问题较多,更加详细的实现可以直接看angular的源码。
以后会逐步介绍angular其它原理。
以上是大佬教程为你收集整理的30行代码让你理解angular依赖注入:angular 依赖注入原理全部内容,希望文章能够帮你解决30行代码让你理解angular依赖注入:angular 依赖注入原理所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。