453 Stimmen

Einfachste / sauberste Möglichkeit, ein Singleton in JavaScript zu implementieren

Was ist der einfachste/sauberste Weg, um das Singleton-Muster in JavaScript zu implementieren?

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

388voto

Christian C. Salvadó Punkte 763569

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'
// ...

70 Stimmen

+1 Ist es nicht ein wenig merkwürdig, nach einem "Singleton-Muster" in einer Sprache mit globalen Variablen zu suchen???

4 Stimmen

Unter Verwendung des Modulmusters könnte ein öffentliches Element auf ein anderes öffentliches Element zugreifen. Das heißt, wie könnte publicMethod1 publicMethod2 aufrufen?

0 Stimmen

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/…

212voto

sebarmeli Punkte 17559

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();

6 Stimmen

Hinweis: Der ursprüngliche Konstruktor kann erneut mit delete instance.constructor gelesen werden: x = SingletonClass.getInstance();delete x.constructor;new x.constructor;

1 Stimmen

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.

4 Stimmen

Riecht mehr nach Fabrik für mich mit dem ganzen "getInstance"-Teil.

123voto

zzzzBov Punkte 166065

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.

Modulmuster:

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.

Singleton-Muster:

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

1 Stimmen

@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.

4 Stimmen

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.

14 Stimmen

@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.

112voto

UtkarshPramodGupta Punkte 6286

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

0 Stimmen

Hallo, kannst du mir helfen, den Unterschied zwischen _instance und Instanz zu verstehen, da ich Instanz verwendet habe und der Code nicht funktioniert hat?

3 Stimmen

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.

7 Stimmen

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.

39voto

Xaqron Punkte 27851

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

8 Stimmen

Einfrieren würde Sinn machen, wenn Singleton ein Wrapper um eine andere Klasse wäre und nur das instance Feld hätte. Da es derzeit ist (instance auf this gesetzt), könnte diese Klasse auch andere Felder haben und einfrieren macht meiner Meinung nach keinen Sinn.

CodeJaeger.com

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.

Powered by:

X