AngularJS之依赖注入模拟实现
一、概述
AngularJS有一经典之处就是依赖注入,对于什么是依赖注入,熟悉spring的同学应该都非常了解了,但,对于前端而言,还是比较新颖的。
依赖注入,简而言之,就是解除硬编码,达到解偶的目的。
下面,我们看看AngularJS中常用的实现方式。
方法一:推断式注入声明,假定参数名称就是依赖的名称。因此,它会在内部调用函数对象的toString()方法,分析并提取出函数参数列表,然后通过$injector将这些参数注入进对象实例。
如下:
//方法一:推断式注入声明,假定参数名称就是依赖的名称。
//因此,它会在内部调用函数对象的toString()方法,分析并提取出函数参数列表,
//然后通过$injector将这些参数注入进对象实例
injector.invoke(function($http,$timeout){
//TODO
});
方法二:行内注入声明,允许我们在函数定义时,直接传入一个参数数组,数组包含了字符串和函数,其中,字符串代表依赖名,函数代表目标函数对象。
如下:
//方法二:行内注入声明,允许我们在函数定义时,直接传入一个参数数组,
//数组包含了字符串和函数,其中,字符串代表依赖名,函数代表目标函数对象。
module.controller('name',['$http','$timeout',function($http,$timeout){
//TODO
}]);
看了上述代码,心中有一疑问,这些是怎么实现的呢?
哈哈,下面,我们就来一起模拟一下这些依赖注入方式,从而了解它们。
二、搭建基本骨架
依赖注入的获取过程就是,通过字段映射,从而获取到相应的方法。
故而,要实现一个基本的依赖注入,我们需要一个存储空间(dependencies),存储所需键值(key/value);一个注册方法(register),用于新增键值对到存储空间中;还有一个就是核心实现方法(resolve),通过相关参数,到存储空间中获得对应的映射结果。
So,基本骨架如下:
varinjector={
dependencies:{},
register:function(key,value){
this.dependencies[key]=value;
returnthis;
},
resolve:function(){
}
};
三、完善核心方法resolve
从我们搭建的基本骨架中,可以发现,重点其实resolve方法,用于实现我们具体形式的依赖注入需求。
首先,我们来实现,如下形式的依赖注入:推断式注入声明。
如下:
injector.resolve(function(Monkey,Dorie){
Monkey();
Dorie();
});
要实现上述效果,我们可以利用函数的toString()方法,我们可以将函数转换成字符串,从而通过正则表达式获得参数名,即key值。然后通过key值,在存储空间dependencies找value值,没找到对应值,则报错。
实现如下:
varinjector={
dependencies:{},
register:function(key,value){
this.dependencies[key]=value;
returnthis;
},
resolve:function(){
varfunc,deps,args=[],scope=null;
func=arguments[0];
//获取函数的参数名
deps=func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(//g,'').split(',');
scope=arguments[1]||{};
for(vari=0,len=deps.length;i<len,d=deps[i];i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
thrownewError('Can\'tfind'+d);
}
}
func.apply(scope,args);
}
};
测试代码,如下:
<!DOCTYPEhtml>
<head>
<metacharset="utf-8"/>
</head>
<body>
<script>
varinjector={
dependencies:{},
register:function(key,value){
this.dependencies[key]=value;
returnthis;
},
resolve:function(){
varfunc,deps,args=[],scope=null;
func=arguments[0];
//获取函数的参数名
deps=func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(//g,'').split(',');
scope=arguments[1]||{};
for(vari=0,len=deps.length;i<len,d=deps[i];i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
thrownewError('Can\'tfind'+d);
}
}
func.apply(scope,args);
}
};
//测试代码
injector.register('Monkey',function(){
console.log('Monkey');
}).register('Dorie',function(){
console.log('Dorie');
});
injector.resolve(function(Monkey,Dorie){
Monkey();
Dorie();
console.log('-.-');
});
</script>
</body>
</html>
推断式注入声明,有个缺点,就是不能利用压缩工具压缩,因为我们是通过函数的参数作为依赖的,当我们压缩时,会将参数名改掉,参数名都变了,那肯定扑街咯。
那么下面,我们就看看行内注入声明,它可以弥补这缺点。
实现行内注入声明,如下:
injector.resolve(['Monkey','Dorie',function(M,D){
M();
D();
}]);
利用typeof判断arguments[0]的类型可以辨别并获得依赖参数和函数。
实现如下:
varinjector={
dependencies:{},
register:function(key,value){
this.dependencies[key]=value;
returnthis;
},
resolve:function(){
varfirstParams,func,deps=[],scope=null,args=[];
firstParams=arguments[0];
scope=arguments[1]||{};
//获得依赖参数
for(vari=0,len=firstParams.length;i<len;i++){
varval=firstParams[i],
type=typeofval;
if(type==='string'){
deps.push(val);
}elseif(type==='function'){
func=val;
}
}
//通过依赖参数,找到关联值
for(i=0,len=deps.length;i<len,d=deps[i];i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
thrownewError('Can\'tfind'+d);
}
}
func.apply(scope||{},args);
}
};
测试代码,如下:
<!DOCTYPEhtml>
<head>
<metacharset="utf-8"/>
</head>
<body>
<script>
varinjector={
dependencies:{},
register:function(key,value){
this.dependencies[key]=value;
returnthis;
},
resolve:function(){
varfirstParams,func,deps=[],scope=null,args=[];
firstParams=arguments[0];
scope=arguments[1]||{};
//获得依赖参数
for(vari=0,len=firstParams.length;i<len;i++){
varval=firstParams[i],
type=typeofval;
if(type==='string'){
deps.push(val);
}elseif(type==='function'){
func=val;
}
}
//通过依赖参数,找到关联值
for(i=0,len=deps.length;i<len,d=deps[i];i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
thrownewError('Can\'tfind'+d);
}
}
func.apply(scope||{},args);
}
};
//测试代码
injector.register('Monkey',function(){
console.log('Monkey');
}).register('Dorie',function(){
console.log('Dorie');
});
injector.resolve(['Monkey','Dorie',function(M,D){
M();
D();
console.log('-.-');
}]);
</script>
</body>
</html>
因为行内注入声明,是通过字符串的形式作为依赖参数,so,压缩也不怕咯。
最后,我们将上面实现的两种方法,整合到一起,就可以为所欲为啦。
那,就合并下吧,如下:
varinjector={
dependencies:{},
register:function(key,value){
this.dependencies[key]=value;
returnthis;
},
resolve:function(){
varfirstParams,func,deps=[],scope=null,args=[];
firstParams=arguments[0];
scope=arguments[1]||{};
//判断哪种形式的注入
if(typeoffirstParams==='function'){
func=firstParams;
deps=func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(//g,'').split(',');
}else{
for(vari=0,len=firstParams.length;i<len;i++){
varval=firstParams[i],
type=typeofval;
if(type==='string'){
deps.push(val);
}elseif(type==='function'){
func=val;
}
}
}
//通过依赖参数,找到关联值
for(i=0,len=deps.length;i<len,d=deps[i];i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
thrownewError('Can\'tfind'+d);
}
}
func.apply(scope||{},args);
}
};
四、花絮—RequireJS之依赖注入
依赖注入并非在AngularJS中有,倘若你使用过RequireJS,那么下面这种形式,不会陌生吧:
require(['Monkey','Dorie'],function(M,D){
//TODO
});
通过,上面我们一步步的模拟AngularJS依赖注入的实现,想必,看到这,你自己也会豁然开朗,换汤不换药嘛。
模拟实现如下:
varinjector={
dependencies:{},
register:function(key,value){
this.dependencies[key]=value;
returnthis;
},
resolve:function(deps,func,scope){
varargs=[];
for(vari=0,len=deps.length;i<len,d=deps[i];i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
thrownewError('Can\'tresolve'+d);
}
}
func.apply(scope||{},args);
}
};
测试代码如下:
<!DOCTYPEhtml>
<head>
<metacharset="utf-8"/>
</head>
<body>
<script>
varinjector={
dependencies:{},
register:function(key,value){
this.dependencies[key]=value;
returnthis;
},
resolve:function(deps,func,scope){
varargs=[];
for(vari=0,len=deps.length;i<len,d=deps[i];i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
thrownewError('Can\'tresolve'+d);
}
}
func.apply(scope||{},args);
}
};
//测试代码
injector.register('Monkey',function(){
console.log('Monkey');
}).register('Dorie',function(){
console.log('Dorie');
});
injector.resolve(['Monkey','Dorie'],function(M,D){
M();
D();
console.log('-.-');
});
</script>
</body>
</html>
五、参考
1、AngularJS应用开发思维之3:依赖注入
2、DependencyinjectioninJavaScript
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。