Die Welt der Computerprogrammierung ist eigentlich ein Prozess der ständigen Abstraking einfacher Teile und der Organisation dieser Abstraktionen. JavaScript ist keine Ausnahme. Wenn wir JavaScript zum Schreiben von Anwendungen verwenden, verwenden wir Code, das von anderen geschrieben wurde, wie z. Wenn unsere Projekte wachsen, müssen wir immer mehr Module verlassen, auf die wir uns verlassen müssen. Zu diesem Zeitpunkt ist es zu einem sehr wichtigen Thema geworden, diese Module effektiv zu organisieren. Die Abhängigkeitsinjektion löst das Problem, wie Codeabhängigkeitsmodule effektiv organisiert werden können. Möglicherweise haben Sie in einigen Frameworks oder Bibliotheken wie dem berühmten Front-End-Framework AngularJs von dem Begriff "Abhängigkeitsinjektion" gehört. Die Abhängigkeitsinjektion ist eines der wichtigsten Merkmale. Die Abhängigkeitsinjektion ist jedoch überhaupt nichts Neues, es gibt es schon lange in anderen Programmiersprachen wie PHP. Gleichzeitig ist die Abhängigkeitsinjektion nicht so kompliziert wie imaginär. In diesem Artikel lernen wir das Konzept der Abhängigkeitsinjektion in JavaScript und erklären auf leicht verständliche Weise, wie man "Abhängigkeitsinjektionsstil" -Codes schreibt.
Zieleinstellung
Angenommen, wir haben jetzt zwei Module. Die Funktion des ersten Moduls besteht darin, AJAX -Anforderungen zu senden, während die Funktion des zweiten Moduls als Routing verwendet wird.
Die Codekopie lautet wie folgt:
var service = function () {
return {name: 'service'};
}
var Router = function () {
return {name: 'Router'};
}
Zu diesem Zeitpunkt haben wir eine Funktion geschrieben, die die beiden oben genannten Module verwenden muss:
Die Codekopie lautet wie folgt:
var dosomething = function (andere) {
var s = service ();
var r = Router ();
};
Um unseren Code interessanter zu machen, muss dieser Parameter mehrere weitere Parameter erhalten. Natürlich können wir den oben genannten Code verwenden, aber aus irgendeinem Aspekt scheint der obige Code etwas weniger flexibel zu sein. Was sollten wir tun, wenn der Modulname, den wir verwenden müssen, ServiceXML oder ServiceJson wird? Oder was ist, wenn wir einige gefälschte Module für Testzwecke verwenden möchten? Zu diesem Zeitpunkt können wir die Funktion selbst nicht einfach bearbeiten. Als erstes müssen wir das abhängige Modul als Parameter an die Funktion übergeben. Der Code lautet wie folgt:
Die Codekopie lautet wie folgt:
var dosomething = function (Service, Router, andere) {
var s = service ();
var r = Router ();
};
Im obigen Code übergeben wir die Module, die wir vollständig brauchen. Aber dies bringt ein neues Problem auf sich. Angenommen, wir nennen die Dosenmethode im Bruder Teil des Codes. Was sollten wir zu diesem Zeitpunkt tun, wenn wir eine dritte Abhängigkeit brauchen? Zu diesem Zeitpunkt ist es keine kluge Art, den gesamten Funktionsaufrufcode zu bearbeiten. Wir brauchen also einen Code, um dies zu tun. Dies ist das Problem, dass der Abhängigkeitsinjektor versucht zu lösen. Jetzt können wir unsere Ziele festlegen:
1. Wir sollten in der Lage sein, Abhängigkeiten zu registrieren
2. Der Abhängigkeitsinjektor sollte eine Funktion empfangen und dann eine Funktion zurückgeben, die die erforderlichen Ressourcen erhalten kann
3. Der Code sollte nicht kompliziert sein, aber einfach und freundlich sein
4. Die Abhängigkeitsinjektor sollte den übergebenen Funktionsbereich beibehalten
5. Die übergebene Funktion sollte in der Lage sein, benutzerdefinierte Parameter zu empfangen, nicht nur die beschriebenen Abhängigkeiten
Anforderungsmethode/AMD -Methode
Vielleicht haben Sie von den berühmten Anforderungen gehört, eine Bibliothek, die das Abhängigkeitsinjektionsproblem gut lösen kann:
Die Codekopie lautet wie folgt:
Define (['Service', 'Router'], Funktion (Service, Router) {
// ...
});
Die Idee von fordertJs ist, dass wir zuerst die erforderlichen Module beschreiben und dann Ihre eigenen Funktionen schreiben sollten. Unter ihnen ist die Reihenfolge der Parameter sehr wichtig. Angenommen, wir müssen ein Modul namens Injector schreiben, das eine ähnliche Syntax implementieren kann.
Die Codekopie lautet wie folgt:
var dosomething = injector.resolve (['Service', 'Router'], Funktion (Service, Router, andere) {
erwarten (service (). name) .to.be ('service');
erwarten (Router (). Name) .to.be ('Router');
erwarten (andere). to.be ('andere');
});
Dosen ("andere");
Bevor Sie fortfahren, können wir im Funktionsgremium von Dosen die Assertion -Bibliothek erwarten. Hier ähnelt TDD (testgetriebene Entwicklung).
Jetzt beginnen wir offiziell, unser Injektormodul zu schreiben. Erstens sollte es ein Monomer sein, damit es in jedem Teil unserer Anwendung die gleiche Funktionalität haben kann.
Die Codekopie lautet wie folgt:
var injector = {
Abhängigkeiten: {},
Register: Funktion (Schlüssel, Wert) {
Diese Abhängigkeiten [Schlüssel] = Wert;
},
Auflösung: Funktion (DEPS, Func, Scope) {
}
}
Dieses Objekt ist sehr einfach, mit nur zwei Funktionen und einer Variablen für Speicherzwecke. Wir müssen das DEPS -Array überprüfen und dann nach der Antwort in den Abhängigkeitsvariablen suchen. Der Rest besteht darin, die .Apply -Methode zu verwenden, um die von uns bestandene Func -Variable aufzurufen:
Die Codekopie lautet wie folgt:
Auflösung: Funktion (DEPS, Func, Scope) {
var args = [];
für (var i = 0; i <deps.length, d = deps [i]; i ++) {
if (this.Dependenzen [d]) {
args.push (this. -Abhängigkeiten [d]);
} anders {
Neuen Fehler werfen ('Can/' t lösen ' + d);
}
}
return function () {
func.apply (Scope || {}, args.concat (array.prototype.slice.call (Argumente, 0)));
}
}
Wenn Sie einen Umfang angeben müssen, wird auch der obige Code normal ausgeführt.
Im obigen Code besteht die Rolle von Array.Prototype.lice.call (Argumente, 0) darin, die Argumentevariable in ein reales Array umzuwandeln. Bisher hat unser Code den Test perfekt bestanden. Das Problem hier ist jedoch, dass wir die erforderlichen Module zweimal schreiben müssen, und wir können sie nicht willkürlich anordnen. Auf die zusätzlichen Parameter folgt immer alle Abhängigkeiten.
Reflexionsmethode
Nach der Erklärung in Wikipedia bezieht sich die Reflexion auf die Tatsache, dass ein Objekt während des Laufs seine eigene Struktur und sein eigenes Verhalten ändern kann. In JavaScript ist es einfach die Möglichkeit, den Quellcode eines Objekts zu lesen und den Quellcode zu analysieren. Oder kehren Sie zu unserer Dosenmethode zurück. Wenn Sie die Methode doomting.toString () nennen, können Sie die folgende Zeichenfolge erhalten:
Die Codekopie lautet wie folgt:
"Funktion (Dienst, Router, andere) {
var s = service ();
var r = Router ();
} "
Auf diese Weise können wir, solange wir diese Methode anwenden, leicht die gewünschten Parameter und vor allem ihre Namen abrufen. Dies ist auch die Methode, die von AngularJS zur Implementierung der Abhängigkeitsinjektion verwendet wird. Im AngularJS -Code können wir den folgenden regulären Ausdruck sehen:
Die Codekopie lautet wie folgt:
/^Funktion/s*[^/(]*/(/s*([^/)]*)/)/m
Wir können die Auflösungsmethode an den folgenden Code ändern:
Die Codekopie lautet wie folgt:
Resolve: function () {
var func, dEPs, Umfang, args = [], self = this;
Func = Argumente [0];
DEPS = func.toString (). Übereinstimmung (/^function/s*[^/(]*/(/s*([^/)]*)/)/m) [1] .Replace (//g, '') .Split (',');
Scope = Argumente [1] || {};
return function () {
var a = array.prototype.slice.call (Argumente, 0);
für (var i = 0; i <deps.length; i ++) {
var d = dEPs [i];
args.push (self.dependenzen [d] && d! = ''? self.dependenzen [d]: a.shift ());
}
func.apply (Scope || {}, args);
}
}
Wir verwenden den oben genannten regulären Ausdruck, um der von uns definierten Funktion zu entsprechen, und wir können das folgende Ergebnis erzielen:
Die Codekopie lautet wie folgt:
["Funktion (Service, Router, andere)", "Service, Router, andere"]
Zu diesem Zeitpunkt brauchen wir nur den zweiten Artikel. Aber sobald wir die zusätzlichen Leerzeichen entfernen und die Schnur in Scheiben schneiden, erhalten wir das DEPS -Array. Der folgende Code ist der Teil, den wir geändert haben:
Die Codekopie lautet wie folgt:
var a = array.prototype.slice.call (Argumente, 0);
...
args.push (self.dependenzen [d] && d! = ''? self.dependenzen [d]: a.shift ());
Im obigen Code durchqueren wir das Abhängigkeitsprojekt, wenn es fehlende Elemente gibt, erhalten wir es aus dem Argumenteobjekt, wenn Teile im Abhängigkeitsprojekt fehlen. Wenn ein Array ein leeres Array ist, kehrt die Verwendung der Schaltmethode nur undefined zurück, ohne einen Fehler zu werfen. Bisher sieht die neue Version von Injector so aus:
Die Codekopie lautet wie folgt:
var dosomething = injector.resolve (Funktion (Service, andere, Router) {
erwarten (service (). name) .to.be ('service');
erwarten (Router (). Name) .to.be ('Router');
erwarten (andere). to.be ('andere');
});
Dosen ("andere");
Im obigen Code können wir die Reihenfolge der Abhängigkeiten nach Belieben verwirren.
Aber nichts ist perfekt. Es gibt ein sehr ernstes Problem mit der Abhängigkeitsinjektion von Reflexionsmethoden. Ein Fehler tritt auf, wenn der Code vereinfacht wird. Dies liegt daran, dass während der Vereinfachung des Codes sich der Name des Parameters ändert, wodurch die Abhängigkeiten gelöst werden. Zum Beispiel:
Die Codekopie lautet wie folgt:
var dosomething = function (e, t, n) {var r = e (); var i = t ()}
Wir brauchen also die folgende Lösung, wie in AngularJs:
Die Codekopie lautet wie folgt:
var dosomething = injector.resolve (['Service', 'Router', Funktion (Service, Router) {
}]);
Dies ist der AMD -Lösung sehr ähnlich, die ich am Anfang gesehen habe, sodass wir die beiden oben genannten Methoden integrieren können, und der endgültige Code lautet wie folgt:
Die Codekopie lautet wie folgt:
var injector = {
Abhängigkeiten: {},
Register: Funktion (Schlüssel, Wert) {
Diese Abhängigkeiten [Schlüssel] = Wert;
},
Resolve: function () {
var func, dEPs, Umfang, args = [], self = this;
if (Typeof Argumente [0] === 'String') {
Func = Argumente [1];
DEPs = Argumente [0] .Replace ( / / g, '') .Split (',');
Scope = Argumente [2] || {};
} anders {
Func = Argumente [0];
DEPS = func.toString (). Übereinstimmung (/^function/s*[^/(]*/(/s*([^/)]*)/)/m) [1] .Replace (//g, '') .Split (',');
Scope = Argumente [1] || {};
}
return function () {
var a = array.prototype.slice.call (Argumente, 0);
für (var i = 0; i <deps.length; i ++) {
var d = dEPs [i];
args.push (self.dependenzen [d] && d! = ''? self.dependenzen [d]: a.shift ());
}
func.apply (Scope || {}, args);
}
}
}
Diese Version der Auflösungsmethode kann zwei oder drei Parameter akzeptieren. Hier ist ein Testcode:
Die Codekopie lautet wie folgt:
var dosomething = injector.resolve ('Router ,, Service', Funktion (a, b, c) {
erwarten (a (). name) .to.be ('Router');
erwarten (b). to.be ('andere');
erwarten (c (). name) .to.be ('service');
});
Dosen ("andere");
Möglicherweise haben Sie bemerkt, dass es nichts zwischen zwei Kommas gibt, und das ist kein Fehler. Diese Stelle bleibt für den anderen Parameter. So steuern wir die Reihenfolge der Parameter.
Abschluss
Im obigen Inhalt haben wir verschiedene Methoden zur Abhängigkeitsinjektion in JavaScript eingeführt. Ich hoffe, dieser Artikel kann Ihnen helfen, die Abhängigkeitsinjektionstechnik zu verwenden und den Code für den Abhängigkeitsinjektionsstil zu schreiben.