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

300

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

Jakub Arnold
quelle
15
Downvote für die akzeptierte Antwort, die überhaupt kein Singleton ist. Es ist nur eine globale Variable.
mlibby
5
Dies ist eine Menge Informationen, zeigt aber wirklich die Unterschiede zwischen den verschiedenen JS-Entwurfsmustern. Es hat mir sehr geholfen: addyosmani.com/resources/essentialjsdesignpatterns/book
Justin

Antworten:

315

Ich denke, der einfachste Weg ist, ein einfaches Objektliteral zu deklarieren:

var myInstance = {
  method1: function () {
    // ...
  },
  method2: function () {
    // ...
  }
};

Wenn Sie private Mitglieder in Ihrer Singleton-Instanz haben möchten, können Sie Folgendes tun:

var myInstance = (function() {
  var privateVar = '';

  function privateMethod () {
    // ...
  }

  return { // public interface
    publicMethod1: function () {
      // all private members are accessible here
    },
    publicMethod2: function () {
    }
  };
})();

Dies wurde genannt das Modul Muster , es im Grunde können Sie private Mitglieder auf ein Objekt kapseln, von Vorteil bei der Verwendung der Einnahme Schließungen .

UPDATE: Ich möchte hinzufügen, dass Sie das Singleton-Objekt mithilfe der ES5- Methode einfrieren können, wenn Sie die Änderung des Singleton-Objekts verhindern möchten Object.freeze.

Dadurch wird das Objekt unveränderlich und es werden keine Änderungen an seiner Struktur und seinen Werten vorgenommen.

Darüber hinaus möchte ich erwähnen, dass Sie mit ES6 sehr einfach einen Singleton mit ES-Modulen darstellen und sogar den privaten Status beibehalten können, indem Sie Variablen im Modulbereich deklarieren :

// my-singleton.js
const somePrivateState = []

function privateFn () {
  // ...
}

export default {
  method1() {
    // ...
  },
  method2() {
    // ...
  }
}

Dann können Sie einfach das Singleton-Objekt importieren, um es zu verwenden:

import myInstance from './my-singleton.js'
// ...
CMS
quelle
46
+1 Ist es nicht ein bisschen seltsam, nach einem "Singleton-Muster" in einer Sprache mit globalen Variablen zu suchen?
Victor
4
Wie würde ein öffentliches Mitglied unter Verwendung des Modulmusters auf ein anderes öffentliches Mitglied zugreifen? Dh wie würde ich publicMethod1anrufen publicMethod2?
typeof
4
@ Tom, ja, das Muster wurde in klassenbasierten OOP-Sprachen geboren - ich erinnere mich an viele Implementierungen, die eine statische getInstanceMethode und einen privaten Konstruktor beinhalteten -, aber IMO, dies ist der "einfachste" Weg, ein Singleton-Objekt zu erstellen in Javascript, und am Ende erfüllt es den gleichen Zweck - ein einzelnes Objekt, das Sie nicht erneut initialisieren können (es gibt keinen Konstruktor, es ist nur ein Objekt) -. Über den Code, den Sie verlinkt haben, gibt es einige Probleme, tauschen Sie die aund bVariablendeklarationen aus und testen Sie a === window. Prost.
CMS
15
@ Victor - Es ist nicht ungewöhnlich, in einer solchen Sprache nach einem "Singleton-Muster" zu suchen. Viele objektorientierte Sprachen verwenden globale Variablen und es werden immer noch Singletons verwendet. Singleton garantiert nicht nur, dass es nur ein Objekt einer bestimmten Klasse gibt. Singleton verfügt über einige weitere Funktionen: 1) Es sollte bei der ersten Verwendung initialisiert werden (was nicht nur eine verzögerte Initialisierung bedeutet, sondern auch garantiert, dass das Objekt wirklich einsatzbereit ist). 2) Es sollte threadsicher sein. Das Modulmuster kann das Singleton-Muster ersetzen, jedoch nur im Browser (und nicht immer).
Skalee
55
Dies sollte nicht die akzeptierte Antwort sein. Dies ist überhaupt kein Singleton! Dies ist nur eine globale Variable. Es gibt eine Welt voller Unterschiede zwischen den beiden.
mlibby
172

Ich denke, der sauberste Ansatz ist so 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;
        }
   };
})();

Anschließend können Sie die Funktion als aufrufen

var test = SingletonFactory.getInstance();
sebarmeli
quelle
4
Anmerkung: Der ursprüngliche Konstruktor kann erneut gelesen werden mit delete instance.constructor:x = SingletonClass.getInstance();delete x.constructor;new x.constructor;
Rob W
var test = SingletonClass.getInstance () - sieht nicht sehr sauber oder JS-ähnlich aus. Andere Seelen, die mit einem = neuen Foo () enden; b = neues Foo (); a === b // true
Matthias
3
Riecht für mich eher nach Fabrik mit dem ganzen Teil "getInstance".
Lajos Meszaros
1
Dies ist kein Singleton, da Sie mit Object.create mehrere Instanzen davon erstellen können.
AndroidDev
Geige ist gebrochen
HoffZ
103

Ich bin nicht sicher, ob ich mit dem Modulmuster einverstanden bin, das als Ersatz für ein Singleton-Muster verwendet wird. Ich habe oft gesehen, wie Singletons an Orten verwendet und missbraucht wurden, an denen sie völlig unnötig sind, und ich bin sicher, dass das Modulmuster viele Lücken füllt, in denen Programmierer sonst einen Singleton verwenden würden, aber das Modulmuster ist kein Singleton.

Modulmuster:

var foo = (function () {
    "use strict";
    function aPrivateFunction() {}
    return { aPublicFunction: function () {...}, ... };
}());

Alles, was im Modulmuster initialisiert wurde, geschieht, wenn Fooes deklariert wird. Zusätzlich kann das Modulmuster verwendet werden, um einen Konstruktor zu initialisieren, der dann mehrmals instanziiert werden kann. Während das Modulmuster das richtige Werkzeug für viele Jobs ist, entspricht es nicht einem Singleton.

Singleton-Muster:

Kurzform
var Foo = function () {
    "use strict";
    if (Foo._instance) {
        //this allows the constructor to be called multiple times
        //and refer to the same instance. Another option is to
        //throw an error.
        return Foo._instance;
    }
    Foo._instance = this;
    //Foo initialization code
};
Foo.getInstance = function () {
    "use strict";
    return Foo._instance || new Foo();
}
lange Form mit Modulmuster
var Foo = (function () {
    "use strict";
    var instance; //prevent modification of "instance" variable
    function Singleton() {
        if (instance) {
            return instance;
        }
        instance = this;
        //Singleton initialization code
    }
    //instance accessor
    Singleton.getInstance = function () {
        return instance || new Singleton();
    }
    return Singleton;
}());

In beiden Versionen des von mir bereitgestellten Singleton-Musters kann der Konstruktor selbst als Accessor verwendet werden:

var a,
    b;
a = new Foo(); //constructor initialization happens here
b = new Foo();
console.log(a === b); //true

Wenn Sie sich mit dem Konstruktor auf diese Weise nicht wohl fühlen, können Sie einen Fehler in die if (instance)Anweisung werfen und sich an die lange Form halten:

var a,
    b;
a = Foo.getInstance(); //constructor initialization happens here
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;
    }
    //if the function wasn't called as a constructor,
    //call it as a constructor and return the result
    if (!(this instanceof Foo)) {
        return new Foo();
    }
    Foo._instance = this;
}
var f = new Foo(); //calls Foo as a constructor
-or-
var f = Foo(); //also calls Foo as a constructor
zzzzBov
quelle
4
Ich habe nie etwas darüber gesagt, dass Singletons eine schlechte oder gute Idee sind. Ich sagte, dass Ihre Implementierung von Singleton weitaus komplizierter ist, als es sein muss, weil Sie Javas Einschränkungen mit dem Muster verwechseln, als ob Sie es überhaupt nicht verstanden hätten. Es ist so, als würde man ein Strategiemuster implementieren, indem man Konstruktorfunktionen und -methoden erstellt, wenn man dafür einfach anonyme Funktionen in Javascript verwenden kann.
Esailija
11
@Esailija, es hört sich für mich so an, als würdest du das Singleton-Muster nicht verstehen. Das Singleton-Muster ist ein Entwurfsmuster, das die Instanziierung einer Klasse auf ein Objekt beschränkt. var singleton = {}passt nicht zu dieser Definition.
zzzzBov
9
Dies gilt aufgrund ihrer Einschränkungen nur für Sprachen wie Java. var singleton = {}So implementieren Sie Singleton in Javascript .
Esailija
2
@Esailija, "In einer prototypbasierten Programmiersprache, in der Objekte, aber keine Klassen verwendet werden ..." JavaScript hat das Konzept von Klassen, sodass es nicht gilt.
zzzzBov
18

In 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
Xaqron
quelle
1
Einfrieren wäre sinnvoll, wenn Singleton eine andere Klasse umhüllen und nur das instanceFeld haben würde. Da es derzeit ( instanceauf this) gesetzt ist, kann diese Klasse auch andere Felder haben und das Einfrieren macht imo keinen Sinn.
Thisismydesign
10

Das Folgende funktioniert in Knoten v6

class Foo {
  constructor(msg) {

    if (Foo.singleton) {
      return Foo.singleton;
    }

    this.msg = msg;
    Foo.singleton = this;
    return Foo.singleton;
  }
}

Wir testen:

const f = new Foo('blah');
const d = new Foo('nope');
console.log(f); // => Foo { msg: 'blah' }
console.log(d); // => Foo { msg: 'blah' }
Daniel
quelle
8

In ES6 ist der richtige Weg, dies zu tun:

class MyClass {
  constructor() {
    if (MyClass._instance) {
      throw new Error("Singleton classes can't be instantiated more than once.")
    }
    MyClass._instance = this;

    // ... your rest of the constructor code goes after this
  }
}

var instanceOne = new MyClass() // Executes succesfully 
var instanceTwo = new MyClass() // Throws error

Wenn Sie nicht möchten, dass bei der Erstellung der zweiten Instanz ein Fehler ausgegeben wird, können Sie einfach die letzte Instanz wie folgt zurückgeben:

class MyClass {
  constructor() {
    if (MyClass._instance) {
      return MyClass._instance
    }
    MyClass._instance = this;

    // ... your rest of the constructor code goes after this
  }
}

var instanceOne = new MyClass()
var instanceTwo = new MyClass()

console.log(instanceOne === instanceTwo) // logs "true"

UtkarshPramodGupta
quelle
Hallo, können Sie mir helfen, den Unterschied zwischen _Instanz und Instanz zu erkennen, da ich Instanz verwendet habe und Code nicht funktioniert hat
Abhinav bhardwaj
Es gibt keinen technischen Unterschied in instanceund _instance. Es ist nur eine Namenskonvention in Programmiersprachen, dass wir private Variablen benennen, denen ein Unterstrich vorangestellt ist. Ich vermute, der Grund dafür, dass Ihr Code nicht funktioniert, ist, dass Sie this.instanceanstelle vonMyClass.instance
UtkarshPramodGupta
7

Es gibt mehrere Möglichkeiten, eine Katze zu häuten :) Je nach Geschmack oder Bedarf können Sie eine der vorgeschlagenen Lösungen anwenden. Ich persönlich greife nach Möglichkeit zur ersten Lösung von CMS (wenn Sie keine Privatsphäre benötigen). Da es sich um die einfachste und sauberste Frage handelte, ist dies der Gewinner. Oder auch:

var myInstance = {}; // done!

Dies (Zitat aus meinem Blog) ...

var SingletonClass = new function() { 
    this.myFunction() { 
        //do stuff 
    } 
    this.instance = 1; 
}

macht nicht viel Sinn (mein Blog-Beispiel auch nicht), weil es keine privaten Vars benötigt, also ist es so ziemlich das Gleiche wie:

var SingletonClass = { 
    myFunction: function () { 
        //do stuff 
    },
    instance: 1 
}
Stoyan
quelle
Code-Snippet 2 enthält einen Syntaxfehler. Sie können nicht schreibenthis.f(){}
Xoxox
7

Ich lehne meine Antwort ab, siehe meine andere .

Normalerweise ist ein Modulmuster (siehe Antwort von CMS), das KEIN Singleton-Muster ist, gut genug. Eines der Merkmale von Singleton ist jedoch, dass seine Initialisierung verzögert wird, bis ein Objekt benötigt wird. Dem Modulmuster fehlt diese Funktion.

Mein Vorschlag (CoffeeScript):

window.singleton = (initializer) ->
  instance = undefined
  () ->
    return instance unless instance is undefined
    instance = initializer()

Welche dazu in JavaScript kompiliert:

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() {
    /* This function should create and initialize singleton. */
    alert("creating");
    return {property1: 'value1', property2: 'value2'};
});


alert(window.iAmSingleton().property2); // "creating" will pop up; then "value2" will pop up
alert(window.iAmSingleton().property2); // "value2" will pop up but "creating" will not
window.iAmSingleton().property2 = 'new value';
alert(window.iAmSingleton().property2); // "new value" will pop up
Skalee
quelle
Warum sollten Sie das Modul laden, wenn es nicht benötigt wird? Wenn Sie das Modul laden müssen, laden Sie das Modul und es wird initialisiert.
Esailija
6

Kurze Antwort:

Da JavaScript nicht blockierend ist, werden Singletons in JavaScript sehr hässlich verwendet. Globale Variablen geben Ihnen auch eine Instanz durch die gesamte Anwendung ohne all diese Rückrufe. Das Modulmuster verbirgt die Interna sanft hinter der Schnittstelle. Siehe @ CMS-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);
    }
  };

};

Verwendungszweck:

var singletonInitializer = function(instanceReady) {
  var preparedObject = {property: 'value'};
  // calling instanceReady notifies singleton that instance is ready to use
  instanceReady(preparedObject);
}
var s = singleton(singletonInitializer);

// get instance and use it
s(function(instance) {
  instance.doSomething();
});

Erläuterung:

Singletons bieten Ihnen mehr als nur eine Instanz für die gesamte Anwendung: Ihre Initialisierung wird bis zur ersten Verwendung verzögert. Dies ist eine wirklich große Sache, wenn Sie sich mit Objekten befassen, deren Initialisierung teuer ist. Teuer bedeutet normalerweise E / A und in JavaScript bedeutet E / A immer Rückrufe.

Vertraue keinen Antworten, die dir eine Schnittstelle geben instance = singleton.getInstance(), sie verfehlen alle den Punkt.

Wenn sie keinen Rückruf annehmen, um ausgeführt zu werden, wenn die Instanz bereit ist, funktionieren sie nicht, wenn der Initialisierer E / A ausführt.

Ja, Rückrufe sehen immer hässlicher aus als Funktionsaufrufe, die sofort eine Objektinstanz zurückgeben. Aber noch einmal: Wenn Sie E / A ausführen, sind Rückrufe obligatorisch. Wenn Sie keine E / A ausführen möchten, ist die Instanziierung billig genug, um sie beim Programmstart auszuführen.

Beispiel 1, billiger Initialisierer:

var simpleInitializer = function(instanceReady) {
  console.log("Initializer started");
  instanceReady({property: "initial value"});
}

var simple = singleton(simpleInitializer);

console.log("Tests started. Singleton instance should not be initalized yet.");

simple(function(inst) {
  console.log("Access 1");
  console.log("Current property value: " + inst.property);
  console.log("Let's reassign this property");
  inst.property = "new value";
});
simple(function(inst) {
  console.log("Access 2");
  console.log("Current property value: " + inst.property);
});

Beispiel 2, Initialisierung mit E / A:

In diesem Beispiel wird setTimeouteine teure E / A-Operation vorgetäuscht. Dies zeigt, warum Singletons in JavaScript wirklich Rückrufe benötigen.

var heavyInitializer = function(instanceReady) {
  console.log("Initializer started");
  var onTimeout = function() {
    console.log("Initializer did his heavy work");
    instanceReady({property: "initial value"});
  };
  setTimeout(onTimeout, 500);
};

var heavy = singleton(heavyInitializer);

console.log("In this example we will be trying");
console.log("to access singleton twice before it finishes initialization.");

heavy(function(inst) {
  console.log("Access 1");
  console.log("Current property value: " + inst.property);
  console.log("Let's reassign this property");
  inst.property = "new value";
});

heavy(function(inst) {
  console.log("Access 2. You can see callbacks order is preserved.");
  console.log("Current property value: " + inst.property);
});

console.log("We made it to the end of the file. Instance is not ready yet.");
Skalee
quelle
Durch Versuche und Schwierigkeiten mit anderen Singleton-Antworten, die es nicht geschafft haben, bin ich auf den resultierenden Code gestoßen, der diesem bemerkenswert ähnlich ist.
Mheyman
Aus dem einen oder anderen Grund ist dies die einzige Antwort, die für mich Sinn macht. Die anderen Antworten erinnern mich alle an die Goon-Show-Episode, in der drei Männer versuchen, eine vier Personen hohe Wand zu erklimmen, indem sie sich rekursiv gegenseitig auf die Schultern klettern.
Tim Ogilvy
Das Calback Stacking ist das, was ich wirklich brauchte! Vielen Dank!!
Tim Ogilvy
Dieser Ansatz gibt Ihnen niemals einen Singleton als: Singleton (SingletonInitializer)! == Singleton (SingletonInitializer) Es handelt sich um zwei verschiedene Instanzen. Die resultierende Funktion, die Sie zurückgegeben haben, kann verwendet werden, um weitere Rückrufe an die Instanz anzuhängen, gibt jedoch nicht unbedingt an, dass nur eine Instanz dieses Typs erstellt werden kann. Welches ist der springende Punkt eines Singleton.
Owen
6

Ich habe dieses Beispiel von JavaScript-Mustern erhalten. Bessere Anwendungen mit Codierungs- und Entwurfsmustern erstellen Von Stoyan Stefanovs Buch Für den Fall, dass Sie eine einfache Implementierungsklasse wie ein Singltone-Objekt benötigen, können Sie die sofortige Funktion wie folgt verwenden:

var ClassName;

(function() {
    var instance;
    ClassName = function ClassName() {
        //If private instance variable already initialized return reference
        if(instance) {
            return instance;   
        }
        //If instance does not created save pointer of original reference
        //to private instance variable. 
        instance = this;

        //All constructor initialization will be here
        // i.e.: 
        this.someProperty = 0;
        this.someMethod = function() {
            //Some action here
        };
    };
}());

Sie können dieses Beispiel anhand des folgenden Testfalls überprüfen:

//Extending defined class like Singltone object using new prototype property
ClassName.prototype.nothing = true;
var obj_1 = new ClassName();
//Extending defined class like Singltone object using new prototype property
ClassName.prototype.everything = true; 
var obj_2 = new ClassName();

//Testing does this two object pointing to same instance
console.log(obj_1 === obj_2); //Result is true, it points to same instance object

//All prototype properites work
//no matter when they were defined
console.log(obj_1.nothing && obj_1.everything 
            && obj_2.nothing && obj_2.everything); //Result true


//Values of properties which is defined inside of constructor
console.log(obj_1.someProperty);// output 0
console.log(obj_2.someProperty);// output 0 
//Changing property value 
obj_1.someProperty = 1;

console.log(obj_1.someProperty);// output 1
console.log(obj_2.someProperty);// output 1

console.log(obj_1.constructor === ClassName); //Output true 

Dieser Ansatz besteht alle Testfälle, während die private statische Implementierung fehlschlägt, wenn die Prototyperweiterung verwendet wird (sie kann behoben werden, ist aber nicht einfach) und die öffentliche statische Implementierung weniger ratsam ist, da die Instanz der Öffentlichkeit zugänglich ist.

jsFiddly Demo.

Khamidulla
quelle
5

Ich glaube, ich habe den saubersten Weg gefunden, um in JavaScript zu programmieren, aber Sie brauchen etwas Fantasie. Ich habe diese Idee von einer Arbeitstechnik im Buch "Javascript the good parts" erhalten.

Anstatt das neue Schlüsselwort zu verwenden, können Sie eine Klasse wie die folgende erstellen:

function Class()
{
    var obj = {}; // Could also be used for inheritence if you don't start with an empty object.

    var privateVar;
    obj.publicVar;

    obj.publicMethod= publicMethod;
    function publicMethod(){} 

    function privateMethod(){} 

    return obj;
}

Sie können das obige Objekt instanziieren, indem Sie sagen:

var objInst = Class(); // !!! NO NEW KEYWORD

Mit dieser Arbeitsmethode können Sie nun einen Singleton wie folgt erstellen:

ClassSingleton = function()
{
    var instance= null;

    function Class() // This is the class like the above one
    {
        var obj = {};
        return obj;
    }

    function getInstance()
    {
        if( !instance )
            instance = Class(); // Again no new keyword;

        return instance;
    }   

    return { getInstance : getInstance };

}();

Jetzt können Sie Ihre Instanz durch Aufrufen erhalten

var obj = ClassSingleton.getInstance();

Ich denke, dies ist der sauberste Weg, da die gesamte "Klasse" nicht einmal zugänglich ist.

David
quelle
Mit dieser Technik können Sie jedoch mehr als eine Instanz haben. Das ist nicht richtig.
nicolascolman
1
Ich würde es nicht glauben, Sie können nicht einmal auf die Klasse zugreifen, ohne getInstance durchlaufen zu haben. Könnten Sie näher darauf eingehen?
David
David Maes Entschuldigung, aber ich habe die Validierung im zweiten Beispiel nicht bemerkt. Ich entschuldige mich.
nicolascolman
4

@CMS und @zzzzBov haben beide wundervolle Antworten gegeben, aber nur um meine eigene Interpretation hinzuzufügen, basierend darauf, dass ich in die Entwicklung von Heavy Node.js aus PHP / Zend Framework gewechselt bin, wo Singleton-Muster üblich waren.

Der folgende, kommentarisch dokumentierte Code basiert auf den folgenden Anforderungen:

  • Es kann nur eine Instanz des Funktionsobjekts instanziiert werden
  • Die Instanz ist nicht öffentlich verfügbar und kann nur über eine öffentliche Methode aufgerufen werden
  • Der Konstruktor ist nicht öffentlich verfügbar und kann nur instanziiert werden, wenn noch keine Instanz verfügbar ist
  • Die Deklaration des Konstruktors muss die Änderung seiner Prototypkette ermöglichen. Dadurch kann der Konstruktor von anderen Prototypen erben und "öffentliche" Methoden für die Instanz anbieten

Mein Code ist dem von @ zzzzBov sehr ähnlich, außer dass ich dem Konstruktor eine Prototypenkette und weitere Kommentare hinzugefügt habe, die denjenigen helfen sollen, die aus PHP oder einer ähnlichen Sprache stammen, die traditionelle OOP in die prototypische Natur von Javascripts zu übersetzen. Es ist vielleicht nicht das "einfachste", aber ich glaube, es ist das richtigste.

// declare 'Singleton' as the returned value of a self-executing anonymous function
var Singleton = (function () {
    "use strict";
    // 'instance' and 'constructor' should not be availble in a "public" scope
    // here they are "private", thus available only within 
    // the scope of the self-executing anonymous function
    var _instance=null;
    var _constructor = function (name) {
        this.name = name || 'default';
    }

    // prototypes will be "public" methods available from the instance
    _constructor.prototype.getName = function () {
        return this.name;
    }

    // using the module pattern, return a static object
    // which essentially is a list of "public static" methods
    return {
        // because getInstance is defined within the same scope
        // it can access the "private" 'instance' and 'constructor' vars
        getInstance:function (name) {
            if (!_instance) {
                console.log('creating'); // this should only happen once
                _instance = new _constructor(name);
            }
            console.log('returning');
            return _instance;
        }
    }

})(); // self execute

// ensure 'instance' and 'constructor' are unavailable 
// outside the scope in which they were defined
// thus making them "private" and not "public"
console.log(typeof _instance); // undefined
console.log(typeof _constructor); // undefined

// assign instance to two different variables
var a = Singleton.getInstance('first');
var b = Singleton.getInstance('second'); // passing a name here does nothing because the single instance was already instantiated

// ensure 'a' and 'b' are truly equal
console.log(a === b); // true

console.log(a.getName()); // "first"
console.log(b.getName()); // also returns "first" because it's the same instance as 'a'

Beachten Sie, dass die selbstausführende anonyme Funktion technisch gesehen selbst ein Singleton ist, wie der von @CMS bereitgestellte Code zeigt. Der einzige Haken dabei ist, dass es nicht möglich ist, die Prototypkette des Konstruktors zu ändern, wenn der Konstruktor selbst anonym ist.

Beachten Sie, dass für Javascript die Konzepte "öffentlich" und "privat" nicht wie in PHP oder Java gelten. Wir haben jedoch den gleichen Effekt erzielt, indem wir die Regeln von Javascript zur Verfügbarkeit des Funktionsumfangs genutzt haben.

talentierte mrjones
quelle
Aus Ihrem Code können mehrere Instanzen erstellt werden:var a = Singleton.getInstance('foo'); var b = new a.constructor('bar');
zzzzBov
@zzzzBov: Ich bekomme nur Fehler, wenn ich das in meiner Geige versuche: jsfiddle.net/rxMu8
cincodenada
4

Ich bin mir nicht sicher, warum niemand dies angesprochen hat, aber Sie können einfach Folgendes tun:

var singleton = new (function() {
  var bar = 123

  this.foo = function() {
    // whatever
  }
})()
Derek Chiang
quelle
Dies scheint eine gute Möglichkeit zu sein, die getInstance-Methode zu überspringen und eine einfachere Lösung zu erhalten. Beachten Sie jedoch, dass der Singleton ausgeführt wird, sobald die Datei analysiert wird. Dies bedeutet, dass DOM-Listener in eine $ (document) .ready-Funktion eingeschlossen werden müssen
HoffZ
4

Die klarste Antwort sollte diese aus dem Buch Learning JavaScript Design Patterns von Addy Osmani sein.

var mySingleton = (function () {
 
  // Instance stores a reference to the Singleton
  var instance;
 
  function init() {
 
    // Singleton
 
    // Private methods and variables
    function privateMethod(){
        console.log( "I am private" );
    }
 
    var privateVariable = "Im also private";
 
    var privateRandomNumber = Math.random();
 
    return {
 
      // Public methods and variables
      publicMethod: function () {
        console.log( "The public can see me!" );
      },
 
      publicProperty: "I am also public",
 
      getRandomNumber: function() {
        return privateRandomNumber;
      }
 
    };
 
  };
 
  return {
 
    // Get the Singleton instance if one exists
    // or create one if it doesn't
    getInstance: function () {
 
      if ( !instance ) {
        instance = init();
      }
 
      return instance;
    }
 
  };
 
})();

令狐 葱
quelle
3

Ich glaube, dies ist der einfachste / sauberste und intuitivste Weg, obwohl ES7 erforderlich ist:

export default class Singleton {

  static instance;

  constructor(){
    if(instance){
      return instance;
    }

    this.state = "duke";
    this.instance = this;
  }

}

Der Quellcode stammt von: adam-bien.com

Alt Eisen
quelle
Dies ist völlig falsch und würde beim Aufrufen einen Fehlernew Singleton()
auslösen
2

Was ist daran falsch?

function Klass() {
   var instance = this;
   Klass = function () { return instance; }
}
Manav
quelle
Test = Klass; t1 = new Test(); t2 = new Test();- Keine Möglichkeit, die Klasse umzubenennen oder einen anderen Namespace auszuwählen.
zzzzBov
2

kann ich meine 5 Münzen legen. Ich habe eine Konstruktorfunktion, z.

var A = function(arg1){
  this.arg1 = arg1  
};

Was ich tun muss, ist, dass nur jedes von dieser CF erstellte Objekt gleich ist.

var X = function(){
  var instance = {};
  return function(){ return instance; }
}();

Prüfung

var x1 = new X();
var x2 = new X();
console.log(x1 === x2)
Olencha
quelle
2

Ich habe festgestellt, dass das Folgende das einfachste Singleton-Muster ist, da die Verwendung des neuen Operators dieses sofort innerhalb der Funktion verfügbar macht , sodass kein Objektliteral zurückgegeben werden muss:

var singleton = new (function () {

  var private = "A private value";
  
  this.printSomething = function() {
      console.log(private);
  }
})();

singleton.printSomething();

Kennzeichen
quelle
2

Hier ist das einfache Beispiel, um das Singleton-Muster in Javascript zu erklären.

 var Singleton=(function(){
      var instance;
      var init=function(){
           return {
             display:function(){
             alert("This is a Singleton patern demo");
              }
            };
           }; 
            return {
              getInstance:function(){
                   if(!instance){
                     alert("Singleton check");
                      instance=init();
                       }
               return instance;
             }
         };

    })();

   // In this call first display alert("Singleton check")
  // and then alert("This is a Singleton patern demo");
  // It means one object is created

    var inst=Singleton.getInstance();
    inst.display();

    // In this call only display alert("This is a Singleton patern demo")
   //  it means second time new object is not created, 
   //  it uses the already created object 

    var inst1=Singleton.getInstance();
    inst1.display();
Sheo Dayal Singh
quelle
1

Ich brauchte mehrere Singletons mit:

  • faule Initialisierung
  • Anfangsparameter

und so kam ich auf Folgendes:

createSingleton ('a', 'add', [1, 2]);
console.log(a);

function createSingleton (name, construct, args) {
    window[name] = {};
    window[construct].apply(window[name], args);
    window[construct] = null;
}

function add (a, b) {
    this.a = a;
    this.b = b;
    this.sum = a + b;
}
  • args muss Array sein, damit dies funktioniert. Wenn Sie leere Variablen haben, übergeben Sie einfach []

  • Ich habe ein Fensterobjekt in der Funktion verwendet, aber ich hätte einen Parameter übergeben können, um meinen eigenen Bereich zu erstellen

  • Name- und Konstruktparameter sind nur Zeichenfolgen, damit window [] funktioniert. Mit einigen einfachen Typprüfungen sind jedoch auch window.name und window.construct möglich.

Fred
quelle
1

Wie wäre es auf diese Weise, nur versichern, dass die Klasse nicht wieder neu sein kann.

Auf diese Weise können Sie die instanceofOperation verwenden. Sie können auch die Prototypenkette verwenden, um die Klasse zu erben. Es handelt sich um eine reguläre Klasse, die jedoch nicht neu erstellt werden kann, wenn Sie die Instanz nur verwenden möchtengetInstance

function CA()
{
    if(CA.instance)
    {
        throw new Error('can not new this class');
    }else{
        CA.instance = this;
    }

}
/**
 * @protected
 * @static
 * @type {CA}
 */
CA.instance = null;
/** @static */
CA.getInstance = function()
{
    return CA.instance;
}

CA.prototype = 
/** @lends CA#*/
{
    func: function(){console.log('the func');}
}
// initilize the instance
new CA();

// test here
var c = CA.getInstance()
c.func();
console.assert(c instanceof CA)
// this will failed
var b = new CA();

Wenn Sie das instanceMitglied nicht aussetzen möchten , schließen Sie es einfach.

wener
quelle
1

Das Folgende ist der Ausschnitt aus meinem Durchgang, um ein Singleton-Muster zu implementieren. Dies kam mir während eines Interviewprozesses in den Sinn und ich hatte das Gefühl, dass ich das irgendwo festhalten sollte.

/*************************************************
   *     SINGLETON PATTERN IMPLEMENTATION          *
   *************************************************/

  //since there are no classes in javascript, every object is technically a singleton
  //if you don't inherit from it or copy from it.
  var single = {};
  //Singleton Implementations
  //Declaring as a Global Object...you are being judged!


  var Logger = function() {
    //global_log is/will be defined in GLOBAL scope here
    if(typeof global_log === 'undefined'){
      global_log = this;
    }
    return global_log;
  };


  //the below 'fix' solves the GLOABL variable problem but
  //the log_instance is publicly available and thus can be 

  //tampered with.
  function Logger() {
    if(typeof Logger.log_instance === 'undefined'){
      Logger.log_instance = this;
    }


    return Logger.log_instance;
   };


  //the correct way to do it to give it a closure!


  function logFactory() {
    var log_instance; //private instance
    var _initLog = function() { //private init method
      log_instance = 'initialized';
      console.log("logger initialized!")
    }
    return {
      getLog : function(){ //the 'privileged' method 
        if(typeof log_instance === 'undefined'){
          _initLog();
        }
        return log_instance;
      }
    };
  }

  /***** TEST CODE ************************************************
  //using the Logger singleton
  var logger = logFactory();//did i just gave LogFactory a closure?
  //create an instance of the logger
  var a = logger.getLog();
  //do some work
  //get another instance of the logger
  var b = logger.getLog();


  //check if the two logger instances are same?
  console.log(a === b); //true
  *******************************************************************/

das gleiche kann auf meiner findet Kern Seite

neugierigsam
quelle
1
function Unicode()
  {
  var i = 0, unicode = {}, zero_padding = "0000", max = 9999;
  //Loop through code points
  while (i < max) {
    //Convert decimal to hex value, find the character, then pad zeroes to the codepoint
    unicode[String.fromCharCode(parseInt(i, 16))] = ("u" + zero_padding + i).substr(-4);
    i = i + 1;
    }

  //Replace this function with the resulting lookup table
  Unicode = unicode;
  }

//Usage
Unicode();
//Lookup
Unicode["%"]; //returns 0025
Paul Sweatte
quelle
1

Ist das nicht auch ein Singleton?

function Singleton() {
    var i = 0;
    var self = this;

    this.doStuff = function () {
        i = i + 1;
        console.log( 'do stuff',i );
    };

    Singleton = function () { return self };
    return this;
}

s = Singleton();
s.doStuff();
Nawal
quelle
1

Sie können dies mit Dekoratoren wie in diesem Beispiel für TypeScript tun:

class YourClass {

    @Singleton static singleton() {}

}

function Singleton(target, name, descriptor) {
    var instance;
    descriptor.value = () => {
        if(!instance) instance = new target;
        return instance;
    };
}

Dann verwenden Sie Ihren Singleton wie folgt:

var myInstance = YourClass.singleton();

Zum jetzigen Zeitpunkt sind Dekoratoren in JavaScript-Engines nicht ohne weiteres verfügbar. Sie müssen sicherstellen, dass in Ihrer JavaScript-Laufzeit Dekoratoren tatsächlich aktiviert sind, oder Compiler wie Babel und TypeScript verwenden.

Beachten Sie auch, dass die Singleton-Instanz "faul" erstellt wird, dh nur, wenn Sie sie zum ersten Mal verwenden.

Vad
quelle
1

Modulmuster: im "besser lesbaren Stil". Sie können leicht erkennen, welche Methoden öffentlich und welche privat sind

var module = (function(_name){
   /*Local Methods & Values*/
   var _local = {
      name : _name,
      flags : {
        init : false
      }
   }

   function init(){
     _local.flags.init = true;
   }

   function imaprivatemethod(){
     alert("hi im a private method");
   }

   /*Public Methods & variables*/

   var $r = {}; //this object will hold all public methods.

   $r.methdo1 = function(){
       console.log("method1 call it");
   }

   $r.method2 = function(){
      imaprivatemethod(); //calling private method
   }

   $r.init = function(){
      inti(); //making init public in case you want to init manually and not automatically
   }

   init(); //automatically calling init method

   return $r; //returning all publics methods

})("module");

Jetzt können Sie öffentliche Methoden wie verwenden

module.method2 (); // -> Ich rufe eine private Methode über eine öffentliche Methodenwarnung auf ("hi im a private method")

http://jsfiddle.net/ncubica/xMwS9/

ncubica
quelle
1

Singleton:

Stellen Sie sicher, dass eine Klasse nur eine Instanz hat, und stellen Sie einen globalen Zugriffspunkt darauf bereit.

Das Singleton-Muster begrenzt die Anzahl der Instanzen eines bestimmten Objekts auf nur eine. Diese einzelne Instanz wird als Singleton bezeichnet.

  • definiert getInstance (), das die eindeutige Instanz zurückgibt.
  • verantwortlich für das Erstellen und Verwalten des Instanzobjekts.

Das Singleton-Objekt wird als sofortige anonyme Funktion implementiert. Die Funktion wird sofort ausgeführt, indem sie in Klammern gefolgt von zwei zusätzlichen Klammern eingeschlossen wird. Es wird anonym genannt, weil es keinen Namen hat.

Beispielprogramm,

var Singleton = (function () {
    var instance;
 
    function createInstance() {
        var object = new Object("I am the instance");
        return object;
    }
 
    return {
        getInstance: function () {
            if (!instance) {
                instance = createInstance();
            }
            return instance;
        }
    };
})();
 
function run() {
 
    var instance1 = Singleton.getInstance();
    var instance2 = Singleton.getInstance();
 
    alert("Same instance? " + (instance1 === instance2));  
}

run()

Mohideen bin Mohammed
quelle
1

Am einfachsten / saubersten bedeutet für mich auch einfach zu verstehen und ohne Schnickschnack, wie in der Java-Version der Diskussion viel diskutiert:

Was ist ein effizienter Weg, um ein Singleton-Muster in Java zu implementieren?

Die Antwort, die aus meiner Sicht am einfachsten / saubersten dort passt, lautet:

https://stackoverflow.com/a/70824/1497139

Und es kann nur teilweise in JavaScript übersetzt werden. Einige der Unterschiede in Javascript sind:

  • Konstruktoren können nicht privat sein
  • Klassen können keine Felder deklariert haben

Angesichts der neuesten ECMA-Syntax ist es jedoch möglich, Folgendes zu erreichen:

Singleton-Muster als JavaScript-Klassenbeispiel

 class Singleton {

  constructor(field1,field2) {
    this.field1=field1;
    this.field2=field2;
    Singleton.instance=this;
  }

  static getInstance() {
    if (!Singleton.instance) {
      Singleton.instance=new Singleton('DefaultField1','DefaultField2');
    }
    return Singleton.instance;
  }
}

Beispiel Verwendung

console.log(Singleton.getInstance().field1);
console.log(Singleton.getInstance().field2);

Beispiel Ergebnis

DefaultField1
DefaultField2
Wolfgang Fahl
quelle
1
function Once() {
    return this.constructor.instance || (this.constructor.instance = this);
}

function Application(name) {
    let app = Once.call(this);

    app.name = name;

    return app;
}

Wenn Sie in Klassen sind:

class Once {
    constructor() {
        return this.constructor.instance || (this.constructor.instance = this);
    }
}

class Application extends Once {
    constructor(name) {
        super();

        this.name = name;
    }
}

Prüfung:

console.log(new Once() === new Once());

let app1 = new Application('Foobar');
let app2 = new Application('Barfoo');

console.log(app1 === app2);
console.log(app1.name); // Barfoo
frasq
quelle
1

Wenn Sie Klassen verwenden möchten:

class Singleton {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    if(this.constructor.instance)
      return this.constructor.instance;
    this.constructor.instance = this;
  }
}
let x = new Singleton('s',1);
let y = new Singleton('k',2);

Die Ausgabe für die oben genannten wird sein:

console.log(x.name,x.age,y.name,y.age) // s 1 s 1

Eine andere Art, Singleton mit der Funktion zu schreiben

function AnotherSingleton (name,age) {
  this.name = name;
  this.age = age;
  if(this.constructor.instance)
    return this.constructor.instance;
  this.constructor.instance = this;
}

let a = new AnotherSingleton('s',1);
let b = new AnotherSingleton('k',2);

Die Ausgabe für die oben genannten wird sein:

console.log(a.name,a.age,b.name,b.age)// s 1 s 1
Sudhee
quelle