Maison > interface Web > js tutoriel > Analyse détaillée de l'exemple de code d'injection de dépendances en JavaScript

Analyse détaillée de l'exemple de code d'injection de dépendances en JavaScript

黄舟
Libérer: 2017-03-14 15:18:54
original
1431 Les gens l'ont consulté

Le monde de la

Programmation informatique est en fait un processus d'abstraction constante de parties simples et d'organisation de ces abstractions. JavaScript ne fait pas exception. Lorsque nous utilisons JavaScript pour écrire des applications, utilisons-nous tous des codes écrits par d'autres, comme certaines bibliothèques open source célèbres ou des frameworks . À mesure que notre projet se développe, de plus en plus de modules sur lesquels nous devons nous appuyer deviennent de plus en plus importants. À l'heure actuelle, la manière d'organiser efficacement ces modules est devenue une question très importante. L'Injection de dépendances résout le problème de l'organisation efficace des modules de dépendance de code. Vous avez peut-être entendu le terme « injection de dépendances » dans certains frameworks ou bibliothèques, comme le fameux Front-end FrameworkAngularJS, où l'injection de dépendances est l'une des fonctionnalités très importantes. Cependant, l'injection de dépendances n'a rien de nouveau. Elle existe depuis longtemps dans d'autres langages de programmation tels que PHP. Dans le même temps, l’injection de dépendances n’est pas aussi compliquée qu’on l’imagine. Dans cet article, nous allons apprendre le concept d'injection de dépendances en JavaScript et expliquer de manière simple et conviviale comment écrire du code "style d'injection de dépendances".

Établissement d'objectifs

Supposons que nous ayons maintenant deux modules. Le premier module est utilisé pour envoyer des requêtes Ajax, tandis que le deuxième module est utilisé comme routeur.

var service = function() {
    return { name: 'Service' };
}
var router = function() {
    return { name: 'Router' };
}
Copier après la connexion

A cette époque, nous avons écrit une fonction , qui nécessite l'utilisation des deux modules cités ci-dessus :

var doSomething = function(other) {
    var s = service();
    var r = router();
};
Copier après la connexion

Ici, pour que nous Le code devient un peu plus intéressant, ce paramètre a besoin de recevoir quelques paramètres supplémentaires. Bien sûr, nous pouvons utiliser complètement le code ci-dessus, mais le code ci-dessus est légèrement moins flexible sous tous ses aspects. Si le nom du module que nous devons utiliser devient Service<a href="//m.sbmmt.com/wiki/1527.html" target="_blank">XML<code>Service<a href="//m.sbmmt.com/wiki/1527.html" target="_blank">XML</a> ou < code>ServiceJSONService<a href="//m.sbmmt.com/wiki/1488.html" target="_blank">JSON</a>Que dois-je faire ? Ou que se passe-t-il si nous voulons utiliser de faux modules à des fins de tests. À ce stade, nous ne pouvons pas simplement modifier la fonction elle-même. Donc la première chose que nous devons faire est de passer les modules dépendants en paramètres à la fonction, le code est le suivant :

Dans le code ci-dessus, nous passons exactement les modules dont nous avons besoin. Mais cela soulève un nouveau problème. Supposons que nous appelions la méthode doSomething dans les deux parties du code. À ce stade, que se passe-t-il si nous avons besoin d'une troisième dépendance. Pour le moment, ce n’est pas une bonne idée de modifier tout le code d’appel de fonction. Par conséquent, nous avons besoin d’un morceau de code pour nous aider à le faire. C’est le problème que l’injecteur de dépendances tente de résoudre. Nous pouvons maintenant définir nos objectifs :

requireMéthode js/AMD

Peut-être avez-vous entendu parler du fameux requirejs, qui est une bibliothèque qui peut très bien résoudre les problèmes d'injection de dépendances :

define([&#39;service&#39;, &#39;router&#39;], function(service, router) {       
    // ...
});
Copier après la connexion

L'idée de requirejs est que nous devons d'abord décrire les modules requis, puis écrire vos propres fonctions. Parmi eux, l’ordre des paramètres est important. Supposons que nous devions écrire un module appelé injector qui implémente une syntaxe similaire.

var doSomething = injector.resolve([&#39;service&#39;, &#39;router&#39;], function(service, router, other) {
    expect(service().name).to.be(&#39;Service&#39;);
    expect(router().name).to.be(&#39;Router&#39;);
    expect(other).to.be(&#39;Other&#39;);
});
doSomething("Other");
Copier après la connexion

Avant de continuer, une chose qui doit être expliquée est que dans le corps de la fonction de doSomething, nous utilisons la bibliothèque d'assertions expect.js pour garantir l'exactitude du code. Il y a quelque chose de similaire à l'idée du TDD (test piloté développement) ici.

Maintenant, nous commençons officiellement à écrire notre module injector. Tout d’abord, il doit s’agir d’un monolithe afin qu’il ait les mêmes fonctionnalités dans chaque partie de notre application.

var injector = {
    dependencies: {},
    register: function(key, value) {
        this.dependencies[key] = value;
    },
    resolve: function(deps, func, scope) {

    }
}
Copier après la connexion

这个对象非常的简单,其中只包含两个函数以及一个用于存储目的的变量。我们需要做的事情是检查deps数组,然后在dependencies变量种寻找答案。剩余的部分,则是使用.apply方法去调用我们传递的func变量:

resolve: function(deps, func, scope) {
    var args = [];
    for(var i=0; i<deps.length, d=deps[i]; i++) {
        if(this.dependencies[d]) {
            args.push(this.dependencies[d]);
        } else {
            throw new Error(&#39;Can\&#39;t resolve &#39; + d);
        }
    }
    return function() {
        func.apply(scope || {}, args.concat(Array.prototype.slice.call(arguments, 0)));
    }        
}
Copier après la connexion

如果你需要指定一个作用域,上面的代码也能够正常的运行。

在上面的代码中,Array.prototype.slice.call(arguments, 0)的作用是将arguments变量转换为一个真正的数组。到目前为止,我们的代码可以完美的通过测试。但是这里的问题是我们必须要将需要的模块写两次,而且不能够随意排列顺序。额外的参数总是排在所有的依赖项之后。

反射(reflection)方法

根据维基百科中的解释,反射(reflection)指的是程序可以在运行过程中,一个对象可以修改自己的结构和行为。在JavaScript中,简单来说就是阅读一个对象的源码并且分析源码的能力。还是回到我们的doSomething方法,如果你调用doSomething.to<a href="//m.sbmmt.com/wiki/57.html" target="_blank">String</a>()方法,你可以获得下面的字符串:

"function (service, router, other) {
    var s = service();
    var r = router();
}"
Copier après la connexion

这样一来,只要使用这个方法,我们就可以轻松的获取到我们想要的参数,以及更重要的一点就是他们的名字。这也是AngularJS实现依赖注入所使用的方法。在AngularJS的代码中,我们可以看到下面的正则表达式

/^function\s*[^\(]*\(\s*([^\)]*)\)/m
Copier après la connexion

我们可以将resolve方法修改成如下所示的代码:

resolve: function() {
    var func, deps, scope, args = [], self = this;
    func = arguments[0];
    deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, &#39;&#39;).split(&#39;,&#39;);
    scope = arguments[1] || {};
    return function() {
        var a = Array.prototype.slice.call(arguments, 0);
        for(var i=0; i<deps.length; i++) {
            var d = deps[i];
            args.push(self.dependencies[d] && d != &#39;&#39; ? self.dependencies[d] : a.shift());
        }
        func.apply(scope || {}, args);
    }        
}
Copier après la connexion

我们使用上面的正则表达式去匹配我们定义的函数,我们可以获取到下面的结果:

["function (service, router, other)", "service, router, other"]
Copier après la connexion

此时,我们只需要第二项。但是一旦我们去除了多余的空格并以,来切分字符串以后,我们就得到了deps数组。下面的代码就是我们进行修改的部分:

var a = Array.prototype.slice.call(arguments, 0);
...
args.push(self.dependencies[d] && d != &#39;&#39; ? self.dependencies[d] : a.shift());
Copier après la connexion

在上面的代码中,我们遍历了依赖项目,如果其中有缺失的项目,如果依赖项目中有缺失的部分,我们就从arguments对象中获取。如果一个数组是空数组,那么使用shift方法将只会返回undefined,而不会抛出一个错误。到目前为止,新版本的injector看起来如下所示:

var doSomething = injector.resolve(function(service, other, router) {
    expect(service().name).to.be(&#39;Service&#39;);
    expect(router().name).to.be(&#39;Router&#39;);
    expect(other).to.be(&#39;Other&#39;);
});
doSomething("Other");
Copier après la connexion

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

但是,没有什么是完美的。反射方法的依赖注入存在一个非常严重的问题。当代码简化时,会发生错误。这是因为在代码简化的过程中,参数的名称发生了变化,这将导致依赖项无法解析。例如:

var doSomething=function(e,t,n){var r=e();var i=t()}
Copier après la connexion

因此我们需要下面的解决方案,就像AngularJS中那样:

var doSomething = injector.resolve([&#39;service&#39;, &#39;router&#39;, function(service, router) {

}]);
Copier après la connexion

这和最一开始看到的AMD的解决方案很类似,于是我们可以将上面两种方法整合起来,最终代码如下所示:

var injector = {
    dependencies: {},
    register: function(key, value) {
        this.dependencies[key] = value;
    },
    resolve: function() {
        var func, deps, scope, args = [], self = this;
        if(typeof arguments[0] === &#39;string&#39;) {
            func = arguments[1];
            deps = arguments[0].replace(/ /g, &#39;&#39;).split(&#39;,&#39;);
            scope = arguments[2] || {};
        } else {
            func = arguments[0];
            deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, &#39;&#39;).split(&#39;,&#39;);
            scope = arguments[1] || {};
        }
        return function() {
            var a = Array.prototype.slice.call(arguments, 0);
            for(var i=0; i<deps.length; i++) {
                var d = deps[i];
                args.push(self.dependencies[d] && d != &#39;&#39; ? self.dependencies[d] : a.shift());
            }
            func.apply(scope || {}, args);
        }        
    }
}
Copier après la connexion

这一个版本的resolve方法可以接受两个或者三个参数。下面是一段测试代码:

var doSomething = injector.resolve(&#39;router,,service&#39;, function(a, b, c) {
    expect(a().name).to.be(&#39;Router&#39;);
    expect(b).to.be(&#39;Other&#39;);
    expect(c().name).to.be(&#39;Service&#39;);
});
doSomething("Other");
Copier après la connexion

你可能注意到了两个逗号之间什么都没有,这并不是错误。这个空缺是留给Other这个参数的。这就是我们控制参数顺序的方法。

结语

在上面的内容中,我们介绍了几种JavaScript中依赖注入的方法,希望本文能够帮助你开始使用依赖注入这个技巧,并且写出依赖注入风格的代码。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal