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


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


254
2017-09-25 20:05


Ursprung


Antworten:


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 accesible here
    },
    publicMethod2: function () {
    }
  };
})();

Dies wurde aufgerufen das ModulmusterIm Prinzip können Sie private Elemente auf einem Objekt kapseln, indem Sie die Verwendung von Verschlüsse.


281
2017-09-25 20:10



Ich denke, der sauberste Ansatz ist etwas wie:

var SingletonClass = (function(){
    function SingletonClass() {
        //do stuff
    }
    var instance;
    return {
        getInstance: function(){
            if (instance == null) {
                instance = new SingletonClass();
                // Hide the constructor so the returned objected can't be new'd...
                instance.constructor = null;
            }
            return instance;
        }
   };
})();

Danach können Sie die Funktion als aufrufen

var test = SingletonClass.getInstance();

149
2018-01-30 12:50



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 Singletons an Orten verwendet und missbraucht wurden, wo sie völlig unnötig sind, und ich bin sicher, dass das Modulmuster viele Lücken füllt, wo Programmierer sonst ein Singleton verwenden würden, wie auch immer das Modulmuster ist nicht ein Singleton.

Modulmuster:

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

Alles, was im Modulmuster initialisiert wird, passiert wann Foo ist erklärt. Darüber hinaus kann das Modulmuster verwendet werden, um einen Konstruktor zu initialisieren, der dann mehrfach instanziiert werden kann. Während das Modulmuster für viele Jobs das richtige Werkzeug ist, ist es nicht mit einem Singleton vergleichbar.

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 Singleton-Musters, das ich zur Verfügung gestellt habe, 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 den Konstruktor nicht auf diese Weise verwenden, können Sie einen Fehler in der if (instance) Aussage, und bleiben Sie bei der Verwendung der langen Form:

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

91
2018-04-12 17:08



Es gibt mehrere Möglichkeiten, eine Katze zu häuten :) Abhängig von Ihrem Geschmack oder spezifischen Bedarf können Sie jede der vorgeschlagenen Lösungen anwenden. Ich persönlich gehe für CMS 'erste Lösung wann immer möglich (wenn Sie Privatsphäre nicht benötigen). Da es sich um die einfachste und sauberste Frage handelte, ist das 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 tut das auch nicht), da es keine privaten vars benötigt, also ist es ungefähr dasselbe wie:

var SingletonClass = { 
    myFunction: function () { 
        //do stuff 
    },
    instance: 1 
}

8
2018-06-28 01:04



Ich verurteile meine Antwort, sehen Sie mein anderer.

Normalerweise ist das Modulmuster (siehe CMS-Antwort), 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. Modulmuster fehlt diese Funktion.

Mein Vorschlag (CoffeeScript):

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

Was dazu in JavaScript kompiliert wurde:

window.singleton = function(initializer) {
    var instance;
    instance = void 0;
    return function() {
        if (instance !== void 0) {
            return instance;
        }
        return instance = initializer();
    };
};

Dann kann ich folgendes machen:

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

7
2017-11-02 22:45



Ich habe dieses Beispiel von JavaScript-Muster Erstellen Sie bessere Anwendungen mit Codierungs- und Entwurfsmustern Von Stojan StefanowBuch für den Fall, dass Sie eine einfache Implementierungsklasse wie 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
        };
    };
}());

Und Sie können dieses Beispiel überprüfen, indem Sie dem Testfall folgen:

//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 Prototyp-Erweiterung verwendet wird (sie kann behoben werden, wird aber nicht einfach sein) und die statische Implementierung ist weniger ratsam, da die Instanz öffentlich zugänglich ist.

jsFiddly Demo.


6
2018-01-21 06:44



Kurze Antwort:

Da JavaScript nicht blockiert ist, sind Singletons in JavaScript wirklich hässlich. Globale Variablen geben Ihnen eine Instanz durch die ganze Anwendung auch ohne all diese Rückrufe, Modul-Muster versteckt sanft Interna hinter der Schnittstelle. Siehe @CMS Antwort.

Aber da du ein Singleton haben wolltest ...

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'};
  // 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 geben Ihnen mehr als nur eine Instanz durch die ganze Anwendung: ihre Initialisierung ist bis zur ersten Verwendung verzögert. Das ist wirklich eine große Sache, wenn Sie mit Objekten arbeiten, deren Initialisierung teuer ist. Teuer bedeutet normalerweise I / O und in JavaScript I / O immer Callbacks.

Vertrauen Sie nicht Antworten, die Ihnen eine Schnittstelle geben instance = singleton.getInstance()Sie alle vermissen den Punkt.

Wenn der Rückruf nicht ausgeführt wird, wenn die Instanz bereit ist, funktionieren sie nicht, wenn der Initializer E / A ausführt.

Ja, Callbacks sehen immer hässlicher aus als der Funktionsaufruf, der die Objektinstanz sofort zurückgibt. Aber noch einmal: Wenn Sie I / O machen, sind Rückrufe obligatorisch. Wenn Sie keine E / A durchführen möchten, ist die Instanziierung kostengünstig genug, um dies beim Programmstart zu tun.

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 I / O:

In diesem Beispiel setTimeout täuscht einen teuren I / O-Vorgang vor. Dies zeigt, warum Singletons in JavaScript wirklich Callbacks 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.");

5
2017-10-18 20:36