Das hat mir geholfen, den Unterschied zu verstehen, dank eines Blogbeitrags von Pascal Precht.
Ein Dienst ist eine Methode eines Moduls, die einen Namen und eine Funktion annimmt, die den Dienst definiert. Sie können diesen bestimmten Dienst in andere Komponenten wie Controller, Direktiven und Filter injizieren und verwenden. Eine Fabrik ist eine Methode eines Moduls, die ebenfalls einen Namen und eine Funktion benötigt, die die Fabrik definiert. Auch sie kann auf die gleiche Weise wie der Dienst injiziert und verwendet werden.
Objekte, die mit new erstellt werden, verwenden den Wert der prototype-Eigenschaft ihrer Konstruktorfunktion als ihren Prototyp, also habe ich den Angular-Code gefunden, der Object.create() aufruft, was meiner Meinung nach die Konstruktorfunktion des Dienstes ist, wenn er instanziiert wird. Allerdings ist eine Factory-Funktion wirklich nur eine Funktion, die aufgerufen wird, weshalb wir ein Objektliteral für die Factory zurückgeben müssen.
Hier ist der Angular 1.5 Code, den ich für die Fabrik gefunden habe:
var needsRecurse = false;
var destination = copyType(source);
if (destination === undefined) {
destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
needsRecurse = true;
}
Angular-Quellcode-Schnipsel für die Funktion factory():
function factory(name, factoryFn, enforce) {
return provider(name, {
$get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
});
}
Es nimmt den Namen und die übergebene Fabrikfunktion und gibt einen Anbieter mit dem gleichen Namen zurück, der eine $get-Methode hat, die unsere Fabrikfunktion ist. Immer wenn Sie den Injektor nach einer bestimmten Abhängigkeit fragen, fragt er den entsprechenden Anbieter nach einer Instanz dieses Dienstes, indem er die $get()-Methode aufruft. Aus diesem Grund ist $get() bei der Erstellung von Providern erforderlich.
Hier ist der Angular 1.5 Code für den Service.
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
Es stellt sich heraus, dass beim Aufruf von service() tatsächlich factory() aufgerufen wird! Es wird jedoch nicht nur die Konstruktorfunktion unseres Dienstes an die Fabrik übergeben, wie sie ist. Es wird auch eine Funktion übergeben, die den Injektor auffordert, ein Objekt mit dem angegebenen Konstruktor zu instanziieren.
Mit anderen Worten, wenn wir MyService irgendwo einfügen, passiert im Code folgendes:
MyServiceProvider.$get(); // return the instance of the service
Um es noch einmal zu wiederholen: Ein Dienst ruft eine Fabrik auf, die eine $get()-Methode des entsprechenden Anbieters ist. Außerdem ist $injector.instantiate() die Methode, die letztendlich Object.create() mit der Konstruktorfunktion aufruft. Aus diesem Grund verwenden wir "this" in Diensten.
Für ES5 spielt es keine Rolle, was wir verwenden: service() oder factory(), es wird immer eine Factory aufgerufen, die einen Provider für unseren Dienst erstellt.
Genau das Gleiche kann man aber auch mit Dienstleistungen machen. Ein Dienst ist eine Konstruktorfunktion, aber das hindert uns nicht daran, Objektliterale zurückzugeben. Wir können also unseren Dienstcode so schreiben, dass er im Grunde genau das Gleiche tut wie unsere Fabrik, oder anders gesagt, wir können einen Dienst als Fabrik schreiben, um ein Objekt zurückzugeben.
Warum empfehlen die meisten Menschen den Einsatz von Fabriken gegenüber Dienstleistungen? Dies ist die beste Antwort, die ich gesehen habe. Sie stammt aus dem Buch von Pawel Kozlowski: Mastering Web Application Development with AngularJS.
Die Fabrikmethode ist die gebräuchlichste Methode, um AngularJS-Abhängigkeitsinjektionssystem. Sie ist sehr flexibel und kann ausgefeilte Erstellungslogik enthalten. Da Fabriken reguläre Funktionen sind, können wir auch die Vorteile eines neuen lexikalischen Bereichs nutzen, um um "private" Variablen zu simulieren. Dies ist sehr nützlich, da wir die Implementierungsdetails eines bestimmten Dienstes verbergen können."