手机版

JavaScript中依赖注入的详细说明

时间:2021-09-09 来源:互联网 编辑:宝哥软件园 浏览:

计算机编程世界实际上是一个抽象简单部分并组织这些抽象的过程。JavaScript也不例外。当我们使用JavaScript编写应用程序时,是否总是使用别人编写的代码,比如一些著名的开源库或框架?随着我们项目的增长,我们需要依赖越来越多的模块。这时,如何有效地组织这些模块就成了一个非常重要的问题。依赖注入解决了如何有效组织代码依赖模块的问题。您可能在一些框架或库中听到过“依赖注入”这个词,比如著名的前端框架AngularJS,依赖注入是最重要的特性之一。然而,依赖注入并不是什么新鲜事。它在PHP等其他编程语言中已经存在了很长时间。同时,依赖注入并没有想象的那么复杂。在本文中,我们将学习JavaScript中依赖注入的概念,并解释如何以简单的方式编写“依赖注入风格”的代码。

目标设定

假设我们现在有两个模块。第一个模块用于发送Ajax请求,而第二个模块用作路由。复制代码如下: VarServiCe=function(){ return { name : ' service ' };} var Router=function(){ return { name : ' Router ' };}这时我们写了一个函数,需要用到上面提到的两个模块:复制代码如下: var do某物=function(other){ var s=service();var r=路由器();};这里,为了使我们的代码有趣,这个参数需要接收更多的参数。当然,我们可以使用上面的代码,但是上面的代码在任何方面都没有那么灵活。如果我们需要使用的模块名改为ServiceXML或ServiceJSON呢?或者如果我们想用一些假模块来测试呢。此时,我们不能只编辑函数本身。因此,我们首先需要做的是将依赖模块作为参数传递给函数,代码如下:复制代码如下: var do某物=function (service,router,other){ var=service();var r=路由器();};在上面的代码中,我们完全传递了我们需要的模块。但这带来了一个新问题。假设我们都在代码的兄弟部分调用了doSomething方法。在这一点上,如果我们需要第三个依赖呢?此时,编辑所有函数调用代码并不是明智的方法。因此,我们需要一段代码来帮助我们做到这一点。这就是喷油器试图解决的问题。现在我们可以设定我们的目标:

1.我们应该能够注册依赖关系。2.依赖注入器应该接收一个函数,然后返回一个可以获取所需资源的函数。3.代码不应该复杂,而应该简单友好。4.依赖注入器应该保持传递的函数范围。5.传递的函数应该能够接收自定义参数,而不仅仅是描述的依赖关系

Requirejs/AMD方法

或许你听说过著名的requirejs,这是一个能很好解决依赖注入问题的库:复制代码如下:Define (['service ',' router'],function (service,router) {//.});requirejs的思想是,我们应该首先描述所需的模块,然后编写自己的函数。其中,参数的顺序非常重要。假设我们需要编写一个名为injector的模块,它可以实现类似的语法。复制代码如下: var do某物=injector。解析(['service ',' router'],函数(service,router,other) {expect (service()。姓名)。to . be(' service ');expect(路由器()。名称). to.be('路由器');期望(其他)成为(“其他”);});做某事(“其他”);在继续之前,需要注意的是,我们在doSomething的函数体中使用了expect.js断言库来确保代码的正确性。这里有点像TDD(测试驱动开发)。

现在我们正式开始编写我们的注射器模块。首先,它应该是一个单体,这样它就可以在我们应用的每个部分都有相同的功能。复制代码如下: variejector={ dependencies 3360 },REGISTER :Function (key,value) {this。依赖项[键]=值;},resolve:函数(deps,func,scope){ 0

}}这个对象非常简单,只包含两个函数和一个变量用于存储。我们需要做的是检查deps数组,然后在deps变量中找到答案。剩下的就是用。apply方法调用我们传递的func变量:复制代码如下: resolve 3360 func(deps,func,scope){ varargs=[];for(var I=0;ideps.length,d=deps[I];I){ if(this . dependencies[d]){ args . push(this . dependencies[d]);} else {抛出新错误('无法解析' d);} } return function(){ func . apply(scope | | { },args . concat(array . prototype . slice . call(arguments,0));} }

如果需要指定一个范围,上面的代码可以正常运行。

在上面的代码中,array . prototype . slice . call(arguments,0)的功能是将arguments变量转换为实数组。到目前为止,我们的代码已经完美地通过了测试。但这里的问题是,我们要把需要的模块写两遍,不能随意安排顺序。额外的参数总是排在所有依赖项之后。

反射法

根据维基百科的解释,反射意味着一个对象可以在程序运行时修改其结构和行为。在JavaScript中,它只是读取对象的源代码并分析源代码的能力。或者回到我们的dosome方法,如果调用doSomething.toString()方法,可以得到如下字符串:复制代码如下:' function (service,router,other){ var=service();var r=路由器();这样,只要我们使用这个方法,我们就可以很容易地得到我们想要的参数,更重要的是,它们的名字。这也是AngularJS用来实现依赖注入的方法。在AngularJS的代码中,我们可以看到如下正则表达式:复制代码如下:/function \ s *[\(]* \(\ s *([\)]*)\)/m我们可以将resolve方法修改为如下所示的代码:

复制代码如下: resolve 3360 function(){ varfunc,deps,scope,args=[],self=thisfunc=参数[0];deps=func.toString()。match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].替换(//g ' ')。split(',');范围=参数[1]| | { };return function(){ var a=Array . prototype . slice . call(参数,0);for(var I=0;ideps.lengthI){ var d=deps[I];args . push(self . dependencies[d]d!='' ?self . dependencies[d]: a . shift());} func.apply(作用域|| {},args);} }

我们用上面的正则表达式来匹配我们定义的函数,可以得到如下结果:复制代码的代码如下: ['函数(service,router,other)',' service,router,other']此时我们只需要第二项。但是一旦我们去掉多余的空间,把字符串分开,我们就得到deps数组。下面的代码是我们修改的部分:复制代码如下: var A=array . prototype . slice . call(arguments,0);args . push(self . dependencies[d]d!='' ?self . dependencies[d]: a . shift());

在上面的代码中,我们已经遍历了依赖项。如果依赖项中有缺失的项,如果依赖项中有缺失的部分,我们将从arguments对象中获取它们。如果数组为空,使用shift方法只会返回undefined,并且不会引发错误。到目前为止,新版本的注射器看起来如下:复制代码如下: var do某事=注射器。解析(功能(服务、其他、路由器){expect(服务()。姓名)。去。be(' service ');expect(路由器()。名称). to.be('路由器');期望(其他)成为(“其他”);});做某事(“其他”);

在上面的代码中,我们可以随意混淆依赖关系的顺序。

然而,没有什么是完美的。反射方法的依赖注入存在严重的问题。当代码简化时,会出现错误。这是因为在代码简化的过程中,参数的名称发生了变化,会导致依赖关系无法解析。例如,复制代码如下: var do某物=function (e,t,n){ var r=e();Var i=t()}因此,我们需要下面的解决方案,就像在AngularJS中一样:复制代码的代码如下: var do something=injector。解析(['服务','路由器',功能(服务,路由器){ 0

}]);这与开头看到的AMD的解决方案非常相似,所以我们可以将以上两种方法进行整合,最终的代码如下:复制代码如下: VAR Injector={ Dependencies 3360 } },Register : Function (key,value) {this。依赖项[键]=值;},resolve: function() { var func,deps,scope,args=[],self=thisif(参数[0]的类型==='string') { func=参数[1];deps=参数[0]。替换(//g ' ')。split(',');scope=参数[2]| | { };} else { func=参数[0];deps=func.toString()。match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].替换(//g ' ')。split(',');范围=参数[1]| | { };} return function(){ var a=Array . prototype . slice . call(参数,0);for(var I=0;ideps.lengthI){ var d=deps[I];args . push(self . dependencies[d]d!='' ?self . dependencies[d]: a . shift());} func.apply(作用域|| {},args);} }}

这个版本的resolve方法可以接受两到三个参数。以下是测试代码:复制代码如下: var do something=injector。解析('路由器,服务',函数(a,b,c) {expect (a)()。姓名)。去。be('路由器');期望成为(“其他”);预期(c)()。名称). to.be('服务');});做某事(“其他”);你可能已经注意到两个逗号之间什么都没有,这不是错误。此空缺为参数“其他”保留。这就是我们如何控制参数的顺序。

标签

在上面的内容中,我们介绍了JavaScript中依赖注入的几种方法,希望本文能够帮助大家开始使用依赖注入技术,用依赖注入风格编写代码。

版权声明:JavaScript中依赖注入的详细说明是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。