Le monde de la programmation informatique est en fait un processus de résumé constamment de parties simples et d'organiser ces abstractions. JavaScript ne fait pas exception. Lorsque nous utilisons JavaScript pour écrire des applications, utilisons-nous du code écrit par d'autres, comme certaines bibliothèques ou frameworks open source célèbres. À mesure que nos projets se développent, de plus en plus de modules sur lesquels nous devons compter. À l'heure actuelle, comment organiser efficacement ces modules est devenu un problème très important. L'injection de dépendance résout le problème de la façon d'organiser efficacement les modules de dépendance de code. Vous avez peut-être entendu parler du terme «injection de dépendance» dans certains cadres ou bibliothèques, tels que le célèbre cadre frontal AngularJS. L'injection de dépendance est l'une des caractéristiques très importantes. Cependant, l'injection de dépendance n'est 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épendance n'est pas aussi compliquée que l'imaginaire. Dans cet article, nous apprendrons le concept d'injection de dépendance en JavaScript et expliquerons de manière facile à comprendre comment rédiger le code "Style d'injection de dépendance".
Paramètre cible
Supposons que nous ayons maintenant deux modules. La fonction du premier module consiste à envoyer des demandes AJAX, tandis que la fonction du deuxième module est d'utiliser comme routage.
La copie de code est la suivante:
var service = function () {
return {name: 'service'};
}
var routeur = fonction () {
return {name: 'router'};
}
Pour le moment, nous avons écrit une fonction qui doit utiliser les deux modules mentionnés ci-dessus:
La copie de code est la suivante:
var doSomething = function (autre) {
var s = service ();
var r = router ();
};
Ici, afin de rendre notre code plus intéressant, ce paramètre doit recevoir plusieurs paramètres supplémentaires. Bien sûr, nous pouvons utiliser le code ci-dessus, mais peu importe de n'importe quel aspect, le code ci-dessus semble un peu moins flexible. Que devons-nous faire si le nom du module que nous devons utiliser devient Servicexml ou ServiceJson? Ou si nous voulons utiliser de faux modules à des fins de test? Pour le moment, nous ne pouvons pas simplement modifier la fonction elle-même. Par conséquent, la première chose que nous devons faire est de passer le module dépendant comme paramètres à la fonction, le code est le suivant:
La copie de code est la suivante:
var doSomething = function (service, routeur, autre) {
var s = service ();
var r = router ();
};
Dans le code ci-dessus, nous passons complètement les modules dont nous avons besoin. Mais cela soulève un nouveau problème. Supposons que nous appelions la méthode du dossier dans la partie frère du code. Pour le moment, que devons-nous faire si nous avons besoin d'une troisième dépendance. Pour le moment, ce n'est pas un moyen sage de modifier tout le code d'appel de fonction. Nous avons donc besoin d'un morceau de code pour nous aider à le faire. C'est le problème que l'injecteur de dépendance essaie de résoudre. Maintenant, nous pouvons fixer nos objectifs:
1. Nous devrions pouvoir enregistrer les dépendances
2. L'injecteur de dépendance doit recevoir une fonction puis renvoyer une fonction qui peut obtenir les ressources requises
3. Le code ne doit pas être compliqué, mais doit être simple et convivial
4. L'injecteur de dépendance doit maintenir la portée de la fonction passée
5. La fonction passée devrait être en mesure de recevoir des paramètres personnalisés, pas seulement les dépendances décrites
Méthode requisejs / AMD
Vous avez peut-être entendu parler des célèbres requises, qui est une bibliothèque qui peut bien résoudre le problème d'injection de dépendance:
La copie de code est la suivante:
Define (['Service', 'Router'], fonction (service, routeur) {
// ...
});
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 très important. Supposons que nous devons écrire un module appelé injecteur qui peut implémenter une syntaxe similaire.
La copie de code est la suivante:
var doSomething = injector.resolve (['service', 'router'], fonction (service, routeur, autre) {
attendre (service (). nom) .to.be ('service');
attendre (router (). nom) .to.be ('routeur');
attendre (autre) .to.be («autre»);
});
Dosomething ("autre");
Avant de continuer, une chose à noter est que dans le corps de fonction de la question de la question, nous utilisons la bibliothèque Assertion attend.js pour assurer l'exactitude du code. Voici un peu similaire à TDD (développement axé sur les tests).
Maintenant, nous commençons officiellement à écrire notre module d'injecteur. Il devrait d'abord être un monomère afin qu'il puisse avoir les mêmes fonctionnalités dans toutes les parties de notre application.
La copie de code est la suivante:
var injecteur = {
dépendances: {},
registre: fonction (clé, valeur) {
this.dependces [key] = valeur;
},
Resolve: Fonction (Deps, Func, Scope) {
}
}
Cet objet est très simple, avec seulement deux fonctions et une variable à des fins de stockage. Ce que nous devons faire, c'est vérifier le tableau DEPS, puis rechercher la réponse dans les variables de dépendances. Le reste consiste à utiliser la méthode .Apply pour appeler la variable FUNC que nous avons réussi:
La copie de code est la suivante:
Resolve: Fonction (Deps, Func, Scope) {
var args = [];
pour (var i = 0; i <depS.length, d = deps [i]; i ++) {
if (this.dependces [d]) {
args.push (this.dependces [d]);
} autre {
lancer une nouvelle erreur ('can /' t résolver '+ d);
}
}
return function () {
func.Apply (scope || {}, args.concat (array.prototype.slice.call (arguments, 0)));
}
}
Si vous devez spécifier une portée, le code ci-dessus s'exécutera également normalement.
Dans le code ci-dessus, le rôle de Array.prototype.slice.Call (Arguments, 0) est de convertir la variable d'arguments en un tableau réel. Jusqu'à présent, notre code a parfaitement réussi le test. Mais le problème ici est que nous devons écrire les modules requis deux fois, et nous ne pouvons pas les organiser arbitrairement. Les paramètres supplémentaires sont toujours suivis par toutes les dépendances.
Méthode de réflexion
Selon l'explication de Wikipedia, la réflexion fait référence au fait qu'un objet peut modifier sa propre structure et son comportement pendant la course. Dans JavaScript, il est simplement la possibilité de lire le code source d'un objet et d'analyser le code source. Ou revenez à notre méthode Dosomething, si vous appelez la méthode dosomething.toString (), vous pouvez obtenir la chaîne suivante:
La copie de code est la suivante:
"Fonction (service, routeur, autre) {
var s = service ();
var r = router ();
} "
De cette façon, tant que nous utilisons cette méthode, nous pouvons facilement obtenir les paramètres que nous voulons, et plus important encore, leurs noms. Il s'agit également de la méthode utilisée par AngularJS pour implémenter l'injection de dépendance. Dans le code angularjs, nous pouvons voir l'expression régulière suivante:
La copie de code est la suivante:
/ ^ fonction / s * [^ / (] * / (/ s * ([^ /)] *) /) / m
Nous pouvons modifier la méthode RESOLVE au code suivant:
La copie de code est la suivante:
résolution: fonction () {
Var Func, Deps, Scope, Args = [], self = this;
func = arguments [0];
deps = func.ToString (). Match (/ ^ function / s * [^ / (] * / (/ s * ([^ /)] *) /) / m) [1] .replace (/ / g, '') .split (',');
SPOPE = Arguments [1] || {};
return function () {
var a = array.prototype.slice.call (arguments, 0);
pour (var i = 0; i <depS.length; i ++) {
var d = DEPS [i];
args.push (self.dependces [d] && d! = ''? self.dependces [d]: a.shift ());
}
func.Apply (scope || {}, args);
}
}
Nous utilisons l'expression régulière ci-dessus pour correspondre à la fonction que nous avons définie, et nous pouvons obtenir le résultat suivant:
La copie de code est la suivante:
["Fonction (service, routeur, autre)", "service, routeur, autre"]
À ce stade, nous n'avons besoin que du deuxième élément. Mais une fois que nous supprimons les espaces supplémentaires et coupez la chaîne avec, nous obtenons le tableau DEPS. Le code suivant est la pièce que nous avons modifiée:
La copie de code est la suivante:
var a = array.prototype.slice.call (arguments, 0);
...
args.push (self.dependces [d] && d! = ''? self.dependces [d]: a.shift ());
Dans le code ci-dessus, nous traversons le projet de dépendance, s'il y a des éléments manquants, s'il manque des pièces dans le projet de dépendance, nous l'obtenons de l'objet Arguments. Si un tableau est un tableau vide, l'utilisation de la méthode Shift ne retournera pas défini sans lancer une erreur. Jusqu'à présent, la nouvelle version de l'injecteur ressemble à ceci:
La copie de code est la suivante:
var doSomething = injector.resolve (fonction (service, autre, routeur) {
attendre (service (). nom) .to.be ('service');
attendre (router (). nom) .to.be ('routeur');
attendre (autre) .to.be («autre»);
});
Dosomething ("autre");
Dans le code ci-dessus, nous pouvons confondre l'ordre des dépendances à volonté.
Mais rien n'est parfait. Il y a un problème très grave avec l'injection de dépendance des méthodes de réflexion. Une erreur se produit lorsque le code est simplifié. En effet, lors de la simplification du code, le nom du paramètre change, ce qui entraînera la résolution des dépendances. Par exemple:
La copie de code est la suivante:
var dosomething = function (e, t, n) {var r = e (); var i = t ()}
Nous avons donc besoin de la solution suivante, comme dans AngularJS:
La copie de code est la suivante:
var doSomething = injector.resolve (['service', 'routeur', fonction (service, routeur) {
}]);
Ceci est très similaire à la solution AMD que j'ai vue au début, nous pouvons donc intégrer les deux méthodes ci-dessus, et le code final est le suivant:
La copie de code est la suivante:
var injecteur = {
dépendances: {},
registre: fonction (clé, valeur) {
this.dependces [key] = valeur;
},
résolution: fonction () {
Var Func, Deps, Scope, Args = [], self = this;
if (typeof arguments [0] === 'String') {
func = arguments [1];
deps = arguments [0] .replace (/ / g, '') .split (',');
SPOPE = Arguments [2] || {};
} autre {
func = arguments [0];
deps = func.ToString (). Match (/ ^ function / s * [^ / (] * / (/ s * ([^ /)] *) /) / m) [1] .replace (/ / g, '') .split (',');
SPOPE = Arguments [1] || {};
}
return function () {
var a = array.prototype.slice.call (arguments, 0);
pour (var i = 0; i <depS.length; i ++) {
var d = DEPS [i];
args.push (self.dependces [d] && d! = ''? self.dependces [d]: a.shift ());
}
func.Apply (scope || {}, args);
}
}
}
Cette version de la méthode Resolve peut accepter deux ou trois paramètres. Voici un code de test:
La copie de code est la suivante:
var doSomething = injector.resolve ('routeur ,, service', fonction (a, b, c) {
attendre (a (). nom) .to.be ('routeur');
attendre (b) .to.be («autre»);
attendre (c (). nom) .to.be ('service');
});
Dosomething ("autre");
Vous avez peut-être remarqué qu'il n'y a rien entre deux virgules, et ce n'est pas une erreur. Cette vacance est laissée pour l'autre paramètre. C'est ainsi que nous contrôlons l'ordre des paramètres.
Conclusion
Dans le contenu ci-dessus, nous avons introduit plusieurs méthodes d'injection de dépendance en JavaScript. J'espère que cet article peut vous aider à commencer à utiliser la technique d'injection de dépendance et à écrire du code de style d'injection de dépendance.