Was ist der einfachste/sauberste Weg, um das Singleton-Muster in JavaScript zu implementieren?
+1 Ist es nicht ein wenig merkwürdig, nach einem "Singleton-Muster" in einer Sprache mit globalen Variablen zu suchen???
Was ist der einfachste/sauberste Weg, um das Singleton-Muster in JavaScript zu implementieren?
Ich denke, der einfachste Weg ist es, ein einfaches Objektliteral zu deklarieren:
var myInstance = {
method1: function () {
// ...
},
method2: function () {
// ...
}
};
Wenn Sie private Elemente auf Ihrer Singleton-Instanz möchten, können Sie etwas wie dies tun:
var myInstance = (function() {
var privateVar = '';
function privateMethod () {
// ...
}
return { // öffentliche Schnittstelle
publicMethod1: function () {
// Alle privaten Elemente sind hier zugänglich
},
publicMethod2: function () {
}
};
})();
Dies wird als das Modulmuster bezeichnet und ermöglicht es im Grunde, private Elemente in einem Objekt zu kapseln, indem die Verwendung von Closures genutzt wird.
Wenn Sie die Änderung des Singleton-Objekts verhindern möchten, können Sie es einfrieren, indem Sie die ES5 Object.freeze
-Methode verwenden.
Dadurch wird das Objekt unveränderlich gemacht und verhindert jede Änderung seiner Struktur und Werte.
Wenn Sie ES6 verwenden, können Sie einen Singleton sehr einfach mit ES-Modulen darstellen und sogar privaten Zustand speichern, indem Sie Variablen im Modul-Bereich deklarieren:
// my-singleton.js
const somePrivateState = []
function privateFn () {
// ...
}
export default {
method1() {
// ...
},
method2() {
// ...
}
}
Dann können Sie das Singleton-Objekt einfach importieren, um es zu verwenden:
import myInstance from './my-singleton.js'
// ...
+1 Ist es nicht ein wenig merkwürdig, nach einem "Singleton-Muster" in einer Sprache mit globalen Variablen zu suchen???
Unter Verwendung des Modulmusters könnte ein öffentliches Element auf ein anderes öffentliches Element zugreifen. Das heißt, wie könnte publicMethod1
publicMethod2
aufrufen?
Ich denke, das Muster besteht darin, nur eine Instanz des Objekts im gesamten Code zu haben, unabhängig davon, wie oft Sie das Objekt initialisieren. Wie ich kommentiert habe (nicht mein Code) unter stackoverflow.com/questions/1635800/…
Ich glaube, der sauberste Ansatz ist etwas wie:
var SingletonFactory = (function(){
function SingletonClass() {
//do stuff
}
var instance;
return {
getInstance: function(){
if (instance == null) {
instance = new SingletonClass();
// Hide the constructor so the returned object can't be new'd...
instance.constructor = null;
}
return instance;
}
};
})();
Danach kannst du die Funktion aufrufen als:
var test = SingletonFactory.getInstance();
Hinweis: Der ursprüngliche Konstruktor kann erneut mit delete instance.constructor
gelesen werden: x = SingletonClass.getInstance();delete x.constructor;new x.constructor;
Var test = SingletonClass.getInstance() - sieht nicht sehr sauber oder JS-ähnlich aus. Andere Lösungen, die zu a = new Foo(); b = new Foo(); a === b //true führen.
Ich bin mir nicht sicher, ob ich damit einverstanden bin, dass das Modulmuster als Ersatz für ein Singleton-Muster verwendet wird. Ich habe oft gesehen, dass Singleton-Muster an Orten verwendet und missbraucht werden, wo sie völlig unnötig sind, und ich bin sicher, dass das Modulmuster viele Lücken füllt, wo Programmierer ansonsten ein Singleton verwenden würden. Das Modulmuster ist jedoch nicht ein Singleton.
var foo = (function () {
"use strict";
function aPrivateFunction() {}
return { aPublicFunction: function () {...}, ... };
}());
Alles, was im Modulmuster initialisiert wird, passiert beim Deklarieren von Foo
. Darüber hinaus kann das Modulmuster verwendet werden, um einen Konstruktor zu initialisieren, der dann mehrmals instanziiert werden könnte. Obwohl das Modulmuster das richtige Werkzeug für viele Aufgaben ist, entspricht es nicht einem Singleton.
Kurzform
var Foo = function () {
"use strict";
if (Foo._instance) {
// Dies ermöglicht es, den Konstruktor mehrmals aufzurufen
// und sich auf dieselbe Instanz zu beziehen. Eine andere Option wäre es,
// einen Fehler zu werfen.
return Foo._instance;
}
Foo._instance = this;
// Foo-Initialisierungscode
};
Foo.getInstance = function () {
"use strict";
return Foo._instance || new Foo();
}
Langform, mit Verwendung des Modulmusters
var Foo = (function () {
"use strict";
var instance; // Verhindert die Änderung der "instance"-Variable
function Singleton() {
if (instance) {
return instance;
}
instance = this;
// Singleton-Initialisierungscode
}
// Instanz-Zugriff
Singleton.getInstance = function () {
return instance || new Singleton();
}
return Singleton;
}());
In beiden Versionen des von mir bereitgestellten Singleton-Musters kann der Konstruktor selbst als Zugriffspunkt verwendet werden:
var a,
b;
a = new Foo(); // Die Initialisierung des Konstruktors erfolgt hier
b = new Foo();
console.log(a === b); // true
Wenn Sie sich nicht wohl dabei fühlen, den Konstruktor auf diese Weise zu verwenden, können Sie einen Fehler im if (instance)
-Statement werfen und sich auf die Verwendung der Langform beschränken:
var a,
b;
a = Foo.getInstance(); // Die Initialisierung des Konstruktors erfolgt hier
b = Foo.getInstance();
console.log(a === b); // true
Ich sollte auch erwähnen, dass das Singleton-Muster gut zum impliziten Konstruktorfunktionsmuster passt:
function Foo() {
if (Foo._instance) {
return Foo._instance;
}
// Wenn die Funktion nicht als Konstruktor aufgerufen wurde,
// rufen Sie sie als Konstruktor auf und geben Sie das Ergebnis zurück
if (!(this instanceof Foo)) {
return new Foo();
}
Foo._instance = this;
}
var f = new Foo(); // Ruft Foo als Konstruktor auf
-oder-
var f = Foo(); // Ruft auch Foo als Konstruktor auf
@Esailija, Ich habe nie gesagt, dass Singletons eine gute Idee sind. Die Frage war, wie man ein Singleton in JavaScript erstellt, und dies ist das Muster, das Sie verwenden würden, um sicherzustellen, dass der Konstruktor nicht mehrmals aufgerufen werden könnte. var singleton = {}
ist nur eine Objektinstanz und ist kein Singleton. Sicherlich könnten Sie dynamisch Eigenschaften hinzufügen, um das Objekt geeignet einzigartig zu machen, aber das macht es nicht zu einem Singleton, denn die Instanz ist von der Object
"Klasse" und Sie könnten mehr als eine erstellen.
Ich habe nie gesagt, dass Singletons eine schlechte oder gute Idee sind. Ich habe gesagt, dass deine Implementierung des Singletons weit komplizierter ist, als sie sein müsste, weil du Javas Beschränkungen mit dem Muster verwechselst, als ob du es überhaupt nicht verstehen würdest. Es ist so, als würde man das Strategiemuster implementieren, indem man Konstruktorfunktionen und Methoden erstellt, wenn man in Javascript einfach anonyme Funktionen dafür verwenden kann.
@Esailija, mir scheint, dass du das Singleton-Muster nicht verstehst. Das Singleton-Muster ist ein Entwurfsmuster, das die Instantiierung einer Klasse auf ein Objekt beschränkt. var singleton = {}
entspricht nicht dieser Definition.
In ES6 ist der richtige Weg, dies zu tun:
class MyClass {
constructor() {
if (MyClass._instance) {
throw new Error("Singleton-Klassen können nicht mehr als einmal instanziiert werden.")
}
MyClass._instance = this;
// ... Der Rest Ihres Konstruktorcodes erfolgt nach diesem
}
}
var instanceOne = new MyClass() // Wird erfolgreich ausgeführt
var instanceTwo = new MyClass() // Gibt Fehler aus
Oder, wenn Sie nicht möchten, dass bei der Erstellung der zweiten Instanz ein Fehler ausgegeben wird, können Sie einfach die letzte Instanz zurückgeben, wie folgt:
class MyClass {
constructor() {
if (MyClass._instance) {
return MyClass._instance
}
MyClass._instance = this;
// ... Der Rest Ihres Konstruktorcodes erfolgt nach diesem
}
}
var instanceOne = new MyClass()
var instanceTwo = new MyClass()
console.log(instanceOne === instanceTwo) // Gibt "true" aus
Hallo, kannst du mir helfen, den Unterschied zwischen _instance und Instanz zu verstehen, da ich Instanz verwendet habe und der Code nicht funktioniert hat?
Es gibt keinen technischen Unterschied zwischen instance
und _instance
. Es ist nur eine Namenskonvention in Programmiersprachen, dass wir private Variablen mit einem Unterstrich voranstellen. Ich vermute, der Grund dafür, dass Ihr Code nicht funktioniert, ist, dass Sie this.instance
anstelle von MyClass.instance
verwenden.
Dies kann durch die Verwendung einer statischen privaten Variable verbessert werden, die die Instanz hält, z.B. static #instance = null;
. Andernfalls kann man einfach die _instance
-Eigenschaft ändern (auf null
setzen), um eine zweite Instanz zu erstellen.
In ECMAScript 2015 (ES6):
class Singleton {
constructor () {
if (!Singleton.instance) {
Singleton.instance = this
}
// Initialize object
return Singleton.instance
}
// Properties & Methods
}
const instance = new Singleton()
Object.freeze(instance)
export default instance
would be translated to:
In ECMAScript 2015 (ES6):
class Singleton {
constructor () {
if (!Singleton.instance) {
Singleton.instance = this
}
// Objekt initialisieren
return Singleton.instance
}
// Eigenschaften & Methoden
}
const instance = new Singleton()
Object.freeze(instance)
export default instance
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
34 Stimmen
Downvote dafür, dass die akzeptierte Antwort überhaupt kein Singleton ist. Es ist einfach eine globale Variable.
7 Stimmen
Dies ist eine Menge Informationen, aber es legt wirklich die Unterschiede zwischen den verschiedenen JS-Designpatterns dar. Es hat mir sehr geholfen: addyosmani.com/resources/essentialjsdesignpatterns/book