Javascript中依赖注入的详细解决方案
目标的设定
假设我们现在有两个模块,第一个模块的功能是发送Ajax请求,第二个模块用作路由。
复制代码代码如下所示:
var服务=函数(){()
返回{名称:服务};
}
var路由器=函数(){()
返回{名称:'router};
}
此时,我们已经编写了一个需要使用上面提到的两个模块的函数:
复制代码代码如下所示:
VaR做=功能(其他){
var =服务();
var =路由器();
};
在这里,为了使我们的代码更有趣,这个参数需要接受一些参数。当然,我们可以用上面的代码完全,但无论如何,上面的代码有点不太灵活。如果我们需要使用的模块的名称更改为servicexml或servicejson或者,如果我们想使用一些假模块来测试,我们怎么做在这一点上,我们不能仅仅编辑函数本身。所以我们首先需要做的是将依赖模块作为参数传递给函数,代码如下所示:
复制代码代码如下所示:
VaR做=功能(服务、路由器、其他){
var =服务();
var =路由器();
};
在上面的代码中,我们完全把我们需要的模块,但这带来了一个新的问题。假设我们叫的做法在代码部分兄弟。在这一点上,如果我们需要第三依赖我们要什么此时,编辑所有函数调用代码不是一种明智的方法,因此我们需要一段代码来帮助我们解决这个问题,这是为了解决依赖注射器的问题,现在我们可以设定目标了:
1。我们应该能够注册一个依赖项。
2。注入器应该接收一个依赖函数,然后返回一个函数来获取所需资源。
三.代码不应该是复杂的,但应该是简单而友好的。
4。依靠喷油器应保持功能域转移
5。传递函数应该能够接收自定义参数,而不仅仅是所描述的依赖项。
requirejs / AMD的方法
你可能听说过著名的requirejs,这是依赖注入的一个很好的解决问题:
复制代码代码如下所示:
定义({服务','router},功能(服务、路由器){
…
});
requirejs的想法是,首先我们应该描述你需要的,然后写下你自己的功能模块。其中,参数的顺序是很重要的。假设我们需要写一个模块称为喷油器,可以实现类似的语法。
复制代码代码如下所示:
VaR做= injector.resolve({服务','router},功能(服务、路由器、其他){
期望(服务()。名称)。To.be(服务的);
期望(路由器()。名称)。To.be('router);
期望(其他)To.be(其他的);
});
做(其他);
在继续之前,有一点要说明的是,我们使用的expect.js断言图书馆在做的函数体来保证代码的正确性。这里有点像TDD(测试驱动开发)的思想。
现在我们正式开始编写喷油器模块。首先,它应该是一个单一的机构,以便它可以在我们的应用程序的所有部分具有相同的功能。
复制代码代码如下所示:
VaR注射器= { {
依赖项:{ },
寄存器:函数(键,值){
这个依赖项{键} =值;
},
解决:功能(依赖、功能、范围){
}
}
这个目标是非常简单的,只包含两个功能和存储的目的变量。我们需要做的是检查该数组,然后寻找相依变量的答案。其余部分使用。使用的方法来调用函数的变量我们穿过:
复制代码代码如下所示:
解决:功能(依赖、功能、范围){
var args = { };
对于(var i = 0;i < deps.length,a DEPS {我};i++){
如果(这个依赖项{ }){
Args.push(这个依赖{d});
{人}
把新的错误(无法解决+ D);
}
}
返回函数(){
Func.apply(范围| | { },args.concat(array.prototype.slice.call(参数0)));
}
}
如果您需要指定一个范围,上面的代码可以正常运行。
在上面的代码中,Array.prototype.slice.call的角色(参数0)是将实参变量为实数数组。到目前为止,我们的代码可以较好的验证。但现在的问题是,我们要写的模块两次不能安排秩序。额外的参数总是在所有的依赖。
反射(反射)法
据维基百科解释反射(反射)是指程序可以运行在一个过程,一个对象可以修改它的结构和行为。在Javascript,那简直是读一个对象的源代码,对源代码的能力。还是回到我们的做法,如果你打电话给dosomething.tostring()方法,你可以得到以下字符串:
复制代码代码如下所示:
函数(服务,路由器,其他){
var =服务();
var =路由器();
}
这样,只要我们用这种方法,我们可以很容易地得到我们想要的参数,更重要的是,他们的名字,这也是用AngularJS实现依赖注入的方法。在AngularJS的代码,我们可以看到下面的正则表达式:
复制代码代码如下所示:
函数({ *({ })})
我们可以将解析方法修改成以下代码:
复制代码代码如下所示:
解析:函数(){
VAR函数,依赖,范围,args =自我=这{ };
参数= { 0 };
稀释每股收益= func.tostring(。比赛( / ^功能 * { ^ )(} * ( *({ ^ )}×),{ 1 }。取代( / / / G M)),分(),。');
参数范围= { 1 } | | { };
返回函数(){
创建一个array.prototype.slice.call(参数0);
对于(var i = 0;i < deps.length;i++){
var a DEPS {我};
Args.push(自我依赖{d} D!= '自我。依赖{d}:a.shift());
}
Func.apply(范围| | { },args);
}
}
我们使用上面的正则表达式来匹配我们定义的函数,我们可以得到如下结果:
复制代码代码如下所示:
函数(服务,路由器,其他)
在这一点上,我们只需要二项。但一旦我们摆脱多余的空间,用它来剪断绳子,我们得到该数组。下面的代码是我们修改的部分:
复制代码代码如下所示:
创建一个array.prototype.slice.call(参数0);
…
Args.push(自我依赖{d} D!= '自我。依赖{d}:a.shift());
在上面的代码中,我们遍历了依赖项项目。如果有缺失的项目,如果我们依赖于项目中缺少的部分,我们将从参数对象中获取它们。如果数组是空数组,那么移位方法只会返回到未定义的位置,不会抛出错误:
复制代码代码如下所示:
VaR做= injector.resolve(功能(服务,其他路由器){
期望(服务()。名称)。To.be(服务的);
期望(路由器()。名称)。To.be('router);
期望(其他)To.be(其他的);
});
做(其他);
在上面的代码中,我们可以随意地区分依赖项的顺序。
但是没有什么是完美的,在反射法的依赖注入中存在一个非常严重的问题,当代码被简化时就会出现错误,这是因为在代码简化的过程中,参数的名称发生变化,这将导致依赖关系不被解析:
复制代码代码如下所示:
VaR做=功能(E,T,N){ var r = E();var i = t()}
所以我们需要以下的解决方案,如在AngularJS:
复制代码代码如下所示:
VaR做= injector.resolve({服务','router功能(服务、路由器){
});
这与第一次看到的AMD解决方案类似,因此我们可以集成以上两种方法,最后的代码如下所示:
复制代码代码如下所示:
VaR注射器= { {
依赖项:{ },
寄存器:函数(键,值){
这个依赖项{键} =值;
},
解析:函数(){
VAR函数,依赖,范围,args =自我=这{ };
如果(typeof论点{ 0 } = 'String'){
参数= { 1 };
稀释每股收益=论点{ 0 }。取代( / /克,),Split(',');
参数范围= { 2 } | | { };
{人}
参数= { 0 };
DEPs = func.toString (.Match (/^functions*{^) (}* (s* ({^)}*), {1}.replace (/g / /m)),.Split (''), ');
参数范围= { 1 } | | { };
}
返回函数(){
创建一个array.prototype.slice.call(参数0);
对于(var i = 0;i < deps.length;i++){
var a DEPS {我};
Args.push(自我依赖{d} D!= '自我。依赖{d}:a.shift());
}
Func.apply(范围| | { },args);
}
}
}
这个版本的解析方法可以接受两个或三个参数:
复制代码代码如下所示:
VaR做= injector.resolve('router,服务,功能(A,B,C){
(一)期望(名称)To.be('router);
期望(B)To.be(其他的);
期望(C()。名称)。To.be(服务的);
});
做(其他);
您可能没有注意到两个逗号之间的关系,这不是错误。这个空缺留给另一个。这是我们控制参数顺序的方式。
后记
在上面的内容中,我们介绍了Javascript中几种依赖注入的方法。我希望本文可以帮助您开始使用依赖注入技术并编写依赖注入样式代码。