Was ist der einfachste/sauberste Weg, um das Singleton-Muster in JavaScript zu implementieren?
Code-Schnipsel 2 enthält einen Syntaxfehler. Du kannst nicht this.f(){}
schreiben.
Was ist der einfachste/sauberste Weg, um das Singleton-Muster in JavaScript zu implementieren?
Es gibt mehr als einen Weg, um ein Problem zu lösen :) Je nach Geschmack oder spezifischer Anforderung können Sie eine der vorgeschlagenen Lösungen anwenden. Ich persönlich bevorzuge Christians C. Salvadós erste Lösung, wann immer möglich (wenn Sie keine Privatsphäre benötigen).
Da die Frage nach der einfachsten und saubersten Lösung gestellt wurde, ist das der Gewinner. Oder sogar:
var myInstance = {}; // Fertig!
Dies (Zitat von meinem Blog)...
var SingletonClass = new function() {
this.myFunction() {
// Mach etwas
}
this.instance = 1;
}
macht nicht viel Sinn (mein Blog-Beispiel auch nicht), weil es keine privaten Variablen benötigt, also ist es ziemlich das gleiche wie:
var SingletonClass = {
myFunction: function () {
// Mach etwas
},
instance: 1
}
Ich widerrufe meine Antwort, siehe meine andere.
Normalerweise ist das Modulmuster (siehe Christian C. Salvadós Antwort), welches nicht das Singleton-Muster ist, in der Regel ausreichend. Eines der Merkmale des Singletons ist jedoch, dass seine Initialisierung aufgeschoben wird, bis das Objekt benötigt wird. Das Modulmuster hat dieses Merkmal nicht.
Mein Vorschlag (CoffeeScript):
window.singleton = (initializer) ->
instance = undefined
() ->
return instance unless instance is undefined
instance = initializer()
Das kompiliert im JavaScript zu folgendem:
window.singleton = function(initializer) {
var instance;
instance = void 0;
return function() {
if (instance !== void 0) {
return instance;
}
return instance = initializer();
};
};
Dann kann ich Folgendes tun:
window.iAmSingleton = singleton(function() {
/* Diese Funktion sollte das Singleton erstellen und initialisieren. */
alert("erstellen");
return {property1: 'Wert1', property2: 'Wert2'};
});
alert(window.iAmSingleton().property2); // "erstellen" wird angezeigt; dann wird "Wert2" angezeigt
alert(window.iAmSingleton().property2); // "Wert2" wird angezeigt, aber "erstellen" wird nicht
window.iAmSingleton().property2 = 'neuer Wert';
alert(window.iAmSingleton().property2); // "neuer Wert" wird angezeigt
Aufgrund der blockierenden Natur von JavaScript sind Singletons in JavaScript wirklich hässlich in der Anwendung. Globale Variablen geben Ihnen auch eine Instanz für die gesamte Anwendung, ohne all diese Callbacks, und das Modulmuster verbirgt sanft Interna hinter der Schnittstelle. Siehe Christian C. Salvadós Antwort.
Aber, da Sie einen Singleton wollten...
var singleton = function(initializer) {
var state = 'initial';
var instance;
var queue = [];
var instanceReady = function(createdInstance) {
state = 'ready';
instance = createdInstance;
while (callback = queue.shift()) {
callback(instance);
}
};
return function(callback) {
if (state === 'initial') {
state = 'waiting';
queue.push(callback);
initializer(instanceReady);
} else if (state === 'waiting') {
queue.push(callback);
} else {
callback(instance);
}
};
};
Verwendung:
var singletonInitializer = function(instanceReady) {
var preparedObject = {property: 'value'};
// Der Aufruf von instanceReady benachrichtigt den Singleton, dass die Instanz einsatzbereit ist
instanceReady(preparedObject);
}
var s = singleton(singletonInitializer);
// Holen Sie sich die Instanz und verwenden Sie sie
s(function(instance) {
instance.doSomething();
});
Singletons geben Ihnen mehr als nur eine Instanz für die gesamte Anwendung: Ihre Initialisierung wird bis zur ersten Verwendung verzögert. Dies ist wirklich wichtig, wenn Sie es mit Objekten zu tun haben, deren Initialisierung teuer ist. Teuer bedeutet in der Regel I/O und in JavaScript bedeutet I/O immer Callbacks.
Vertrauen Sie nicht auf Antworten, die Ihnen eine Schnittstelle wie instance = singleton.getInstance()
geben, sie verfehlen alle den Punkt.
Wenn sie keinen Callback akzeptieren, der ausgeführt wird, wenn eine Instanz einsatzbereit ist, funktionieren sie nicht, wenn der Initialisierer I/O durchführt.
Ja, Callbacks sehen immer hässlicher aus als ein Funktionsaufruf, der sofort eine Objektinstanz zurückgibt. Aber noch einmal: Wenn Sie I/O durchführen, sind Callbacks obligatorisch. Wenn Sie keine I/O machen möchten, ist die Instantiierung billig genug, um sie zu Programmstart durchzuführen.
var simpleInitializer = function(instanceReady) {
console.log("Initialisierer gestartet");
instanceReady({property: "Anfangswert"});
}
var simple = singleton(simpleInitializer);
console.log("Tests gestartet. Singleton-Instanz sollte noch nicht initialisiert sein.");
simple(function(inst) {
console.log("Zugriff 1");
console.log("Aktueller Eigenschaftswert: " + inst.property);
console.log("Lassen Sie uns diese Eigenschaft neu zuweisen");
inst.property = "neuer Wert";
});
simple(function(inst) {
console.log("Zugriff 2");
console.log("Aktueller Eigenschaftswert: " + inst.property);
});
In diesem Beispiel simuliert setTimeout
eine teure I/O-Operation. Dies verdeutlicht, warum Singletons in JavaScript wirklich Callbacks benötigen.
var heavyInitializer = function(instanceReady) {
console.log("Initialisierer gestartet");
var onTimeout = function() {
console.log("Initialisierer hat seine schwere Arbeit gemacht");
instanceReady({property: "Anfangswert"});
};
setTimeout(onTimeout, 500);
};
var heavy = singleton(heavyInitializer);
console.log("In diesem Beispiel werden wir versuchen,");
console.log("auf den Singleton zweimal zuzugreifen, bevor die Initialisierung abgeschlossen ist.");
heavy(function(inst) {
console.log("Zugriff 1");
console.log("Aktueller Eigenschaftswert: " + inst.property);
console.log("Lassen Sie uns diese Eigenschaft neu zuweisen");
inst.property = "neuer Wert";
});
heavy(function(inst) {
console.log("Zugriff 2. Sie können sehen, dass die Callback-Reihenfolge erhalten bleibt.");
console.log("Aktueller Eigenschaftswert: " + inst.property);
});
console.log("Wir haben es bis zum Ende der Datei geschafft. Die Instanz ist noch nicht bereit.");
Durch Prüfungen und Schwierigkeiten mit anderen einzelnen Antworten, die nicht ausreichten, landete ich schließlich auf einem Ergebniscode, der bemerkenswert ähnlich zu diesem war.
Aus einem Grund oder einem anderen ist dies die einzige Antwort, die für mich Sinn macht. Die anderen Antworten erinnern mich alle an die Folge der Goon Show, in der drei Männer versuchen, eine vier Personen hohe Wand zu erklimmen, indem sie sich gegenseitig auf den Schultern klettern.
Ich denke, ich habe den saubersten Weg gefunden, um in JavaScript zu programmieren, aber du wirst etwas Vorstellungskraft brauchen. Ich habe diese Idee aus einer funktionierenden Technik im Buch _JavaScript: The Good Parts_.
Anstatt das new Schlüsselwort zu verwenden, könntest du eine Klasse so erstellen:
function Class()
{
var obj = {}; // Könnte auch für Vererbung verwendet werden, wenn du nicht mit einem leeren Objekt beginnst.
var privateVar;
obj.publicVar;
obj.publicMethod = publicMethod;
function publicMethod(){}
function privateMethod(){}
return obj;
}
Du kannst das obenstehende Objekt instanziieren, indem du sagst:
var objInst = Klasse(); // !!! KEIN NEW-SCHLÜSSELWORT
Jetzt, mit dieser Arbeitsmethode im Hinterkopf, könntest du einen Singleton so erstellen:
ClassSingleton = function()
{
var instance = null;
function Class() // Das ist die Klasse wie die obige
{
var obj = {};
return obj;
}
function getInstance()
{
if( !instance )
instance = Class(); // Wieder kein 'new'-Schlüsselwort;
return instance;
}
return { getInstance : getInstance };
}();
Jetzt kannst du deine Instanz bekommen, indem du folgendes aufrufst:
var obj = ClassSingleton.getInstance();
Ich denke, das ist der sauberste Weg, da die komplette "Klasse" nicht einmal zugänglich ist.
Ich denke nicht, dass du auch ohne über getInstance zugreifen kannst. Könntest du das genauer erklären?
Die klarste Antwort sollte diese aus dem Buch Learning JavaScript Design Patterns von Addy Osmani sein.
var mySingleton = (function () {
// Instanz speichert eine Referenz auf das Singleton
var instance;
function init() {
// Singleton
// Private Methoden und Variablen
function privateMethod(){
console.log( "Ich bin privat" );
}
var privateVariable = "Ich bin auch privat";
var privateRandomNumber = Math.random();
return {
// Öffentliche Methoden und Variablen
publicMethod: function () {
console.log( "Die Öffentlichkeit kann mich sehen!" );
},
publicProperty: "Ich bin auch öffentlich",
getRandomNumber: function() {
return privateRandomNumber;
}
};
};
return {
// Gib die Singleton-Instanz zurück, wenn eine existiert
// oder erstelle eine neue, falls keine vorhanden ist
getInstance: function () {
if ( !instance ) {
instance = init();
}
return 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