Manière la plus simple / la plus propre d'implémenter singleton en JavaScript?

300

Quelle est la façon la plus simple / la plus propre d'implémenter un modèle singleton en JavaScript?

Jakub Arnold
la source
15
Downvote pour la réponse acceptée n'étant pas du tout un singleton. C'est juste une variable globale.
mlibby
5
C'est une tonne d'informations, mais expose vraiment les différences entre les différents modèles de conception JS. Cela m'a beaucoup aidé: addyosmani.com/resources/essentialjsdesignpatterns/book
Justin

Réponses:

315

Je pense que la façon la plus simple est de déclarer un objet littéral simple:

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

Si vous voulez des membres privés sur votre instance singleton, vous pouvez faire quelque chose comme ceci:

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

  function privateMethod () {
    // ...
  }

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

Cela a été appelé le modèle de module , il vous permet essentiellement d'encapsuler des membres privés sur un objet, en tirant parti de l'utilisation de fermetures .

MISE À JOUR: Je voudrais ajouter que si vous voulez empêcher la modification de l'objet singleton, vous pouvez le geler , en utilisant la Object.freezeméthode ES5 .

Cela rendra l'objet immuable, empêchant toute modification de sa structure et de ses valeurs.

De plus, je voudrais mentionner que si vous utilisez ES6, vous pouvez représenter un singleton à l'aide de modules ES très facilement, et vous pouvez même conserver un état privé en déclarant des variables à la portée du module :

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

function privateFn () {
  // ...
}

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

Ensuite, vous pouvez simplement importer l'objet singleton pour l'utiliser:

import myInstance from './my-singleton.js'
// ...
CMS
la source
46
+1 N'est-ce pas un peu étrange de chercher un "pattern Singleton" dans une langue avec des variables globales ???
Victor
4
En utilisant le modèle de module, comment un membre public pourrait-il accéder à un autre membre public? C'est-à-dire, comment publicMethod1appellerait-on publicMethod2?
type du
4
@ Tom, oui, le modèle est né sur des langages OOP basés sur des classes -Je me souviens de beaucoup d'implémentations qui impliquaient une getInstanceméthode statique et un constructeur privé-, mais IMO, c'est la façon la plus "simple" de construire un objet singleton en Javascript, et à la fin il répond au même but -un seul objet, que vous ne pouvez pas initialiser à nouveau (il n'y a pas de constructeur, c'est juste un objet) -. A propos du code lié, il a quelques problèmes, échanger les aet bdéclarations de variables et essai a === window. À votre santé.
CMS
15
@Victor - Ce n'est pas étrange de chercher un "motif singleton" dans une telle langue. de nombreux langages orientés objet utilisent des variables globales et des singletons sont toujours utilisés. Singleton ne garantit pas seulement qu'il n'y aura qu'un seul objet d'une classe donnée. Singleton a quelques fonctionnalités supplémentaires: 1) il devrait être initialisé lors de la première utilisation (ce qui signifie non seulement une initialisation retardée, mais aussi garantir que l'objet est vraiment prêt à être utilisé) 2) il devrait être thread-safe. Le modèle de module peut remplacer le modèle singleton, mais uniquement dans le navigateur (et pas toujours).
skalee
55
Cela ne devrait pas être la réponse acceptée. Ce n'est pas du tout un singleton! Ceci est juste une variable globale. Il y a un monde de différence entre les deux.
mlibby
172

Je pense que l'approche la plus propre est quelque chose comme:

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

Ensuite, vous pouvez appeler la fonction comme

var test = SingletonFactory.getInstance();
sebarmeli
la source
4
Remarque: Le constructeur d'origine peut être lu à nouveau en utilisant delete instance.constructor:x = SingletonClass.getInstance();delete x.constructor;new x.constructor;
Rob W
var test = SingletonClass.getInstance () - ne semble pas très propre ou comme JS. D'autres soultions qui se retrouvent avec a = new Foo (); b = nouveau Foo (); a === b // true
Matthias
3
Ça sent plus comme l'usine avec toute la partie "getInstance".
Lajos Meszaros
1
Ce n'est pas un singleton car vous pouvez en créer plusieurs instances avec Object.create.
AndroidDev
Fiddle is broken
HoffZ
103

Je ne suis pas sûr d'être d'accord avec le modèle de module utilisé en remplacement d'un modèle singleton. J'ai souvent vu des singletons utilisés et abusés dans des endroits où ils sont totalement inutiles, et je suis sûr que le modèle de module comble de nombreuses lacunes où les programmeurs utiliseraient autrement un singleton, mais le modèle de module n'est pas un singleton.

modèle de module:

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

Tout initialisé dans le modèle de module se produit lorsque Foo est déclaré. De plus, le modèle de module peut être utilisé pour initialiser un constructeur, qui pourrait ensuite être instancié plusieurs fois. Bien que le modèle de module soit le bon outil pour de nombreux travaux, il n'est pas équivalent à un singleton.

motif singleton:

forme courte
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();
}
forme longue, en utilisant le modèle de module
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;
}());

Dans les deux versions du modèle Singleton que j'ai fournies, le constructeur lui-même peut être utilisé comme accesseur:

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

Si vous ne vous sentez pas à l'aise d'utiliser le constructeur de cette façon, vous pouvez lancer une erreur dans l' if (instance)instruction et vous en tenir au formulaire long:

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

Je dois également mentionner que le modèle singleton correspond bien au modèle de fonction constructeur implicite:

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
la source
4
Je n'ai jamais dit que les singletons étaient une mauvaise ou une bonne idée. J'ai dit que votre implémentation de singleton est beaucoup plus compliquée qu'elle ne devrait l'être parce que vous confondez les limitations de Java avec le modèle comme si vous ne le compreniez pas du tout. C'est comme implémenter un modèle de stratégie en créant des fonctions et des méthodes constructeurs lorsque vous pouvez simplement utiliser des fonctions anonymes pour cela en Javascript.
Esailija
11
@Esailija, il me semble que vous ne comprenez pas le motif singleton. Le modèle singleton est un modèle de conception qui limite l'instanciation d'une classe à un seul objet. var singleton = {}ne correspond pas à cette définition.
zzzzBov
9
Cela ne s'applique qu'aux langages comme Java en raison de leurs limites. var singleton = {}est la façon dont vous implémentez singleton en Javascript .
Esailija
2
@Esailija, "Dans un langage de programmation basé sur des prototypes, où des objets mais pas des classes sont utilisés ..." JavaScript a le concept de classes, donc cela ne s'applique pas.
zzzzBov
18

Dans 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
la source
1
Le gel aurait du sens si Singleton était un wrapper autour d'une autre classe et n'avait que le instancechamp. Comme elle est actuellement ( instancedéfinie sur this), cette classe peut également avoir d'autres champs et le gel n'a pas de sens imo.
thisismydesign
10

Les travaux suivants dans le nœud v6

class Foo {
  constructor(msg) {

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

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

Nous testons:

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

Dans ES6, la bonne façon de procéder est:

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

Ou, si vous ne voulez pas qu'une erreur soit levée lors de la création de la deuxième instance, vous pouvez simplement renvoyer la dernière instance, comme ceci:

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
la source
Salut, pouvez-vous m'aider à connaître la différence entre _instance et instance car j'utilisais l'instance et le code ne fonctionnait pas
Abhinav bhardwaj
Il n'y a aucune différence technique dans instanceet _instance. C'est juste une convention de dénomination dans les langages de programmation que nous nommons les variables privées précédées d'un trait de soulignement. Je soupçonne que la raison pour laquelle votre code ne fonctionne pas est que vous utilisez this.instanceau lieu deMyClass.instance
UtkarshPramodGupta
7

Il existe plusieurs façons de dépouiller un chat :) Selon votre goût ou vos besoins spécifiques, vous pouvez appliquer l'une des solutions proposées. Personnellement, je choisis la première solution de CMS chaque fois que cela est possible (lorsque vous n'avez pas besoin de confidentialité). Étant donné que la question portait sur le plus simple et le plus propre, c'est le gagnant. Ou même:

var myInstance = {}; // done!

Ceci (citation de mon blog) ...

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

n'a pas beaucoup de sens (mon exemple de blog non plus) car il n'a pas besoin de vars privés, c'est donc à peu près la même chose que:

var SingletonClass = { 
    myFunction: function () { 
        //do stuff 
    },
    instance: 1 
}
Stoyan
la source
L'extrait de code 2 contient une erreur de syntaxe. Vous ne pouvez pas écrirethis.f(){}
xoxox
7

Je déconseille ma réponse, voir mon autre .

Habituellement, le modèle de module (voir la réponse de CMS) qui n'est PAS un modèle singleton est assez bon. Cependant, l'une des caractéristiques de singleton est que son initialisation est retardée jusqu'à ce que l'objet soit nécessaire. Le modèle de module n'a pas cette fonction.

Ma proposition (CoffeeScript):

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

Qui a compilé cela en JavaScript:

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

Ensuite, je peux faire ce qui suit:

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
la source
Pourquoi voudriez-vous charger le module s'il n'est pas nécessaire? Et lorsque vous devez charger le module, vous chargez le module et il s'initialise.
Esailija
6

Réponse courte:

Du fait de la nature non bloquante de JavaScript, les singletons en JavaScript sont vraiment laids à utiliser. Les variables globales vous donneront également une instance dans toute l'application sans tous ces rappels, le modèle de module cache doucement les internes derrière l'interface. Voir la réponse @CMS.

Mais comme tu voulais un singleton…

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

};

Usage:

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

Explication:

Les singletons vous offrent plus d'une instance dans toute l'application: leur initialisation est retardée jusqu'à la première utilisation. C'est vraiment très important lorsque vous traitez des objets dont l'initialisation est coûteuse. Cher signifie généralement des E / S et en JavaScript, les E / S signifient toujours des rappels.

Ne vous fiez pas aux réponses qui vous donnent une interface comme instance = singleton.getInstance() , elles manquent toutes le point.

S'ils ne prennent pas le rappel pour être exécuté lorsque l'instance est prête, ils ne fonctionneront pas lorsque l'initialiseur effectue des E / S.

Oui, les rappels semblent toujours plus laids que l'appel de fonction qui renvoie immédiatement une instance d'objet. Mais encore une fois: lorsque vous effectuez des E / S, les rappels sont obligatoires. Si vous ne voulez pas faire d'E / S, l'instanciation est assez bon marché pour le faire au démarrage du programme.

Exemple 1, initialiseur bon marché:

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

Exemple 2, initialisation avec E / S:

Dans cet exemple, setTimeoutsimule une opération d'E / S coûteuse. Cela illustre pourquoi les singletons en JavaScript ont vraiment besoin de rappels.

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
la source
À travers des essais et des tribulations avec d'autres réponses singleton qui ne l'ont pas coupé, j'ai atterri sur un code résultant remarquablement similaire à celui-ci.
mheyman
Pour une raison ou une autre, c'est la seule réponse qui me semble logique. Les autres réponses me rappellent toutes l'épisode de l'émission goon où trois hommes tentent de grimper un mur de quatre personnes de haut, en grimpant les uns sur les autres de manière récursive.
Tim Ogilvy
L'empilement de calback est la chose dont j'avais vraiment besoin! Merci!!
Tim Ogilvy
Cette approche ne vous donne jamais réellement un singleton comme: singleton (singletonInitializer)! == singleton (singletonInitializer) ce sont deux instances différentes. La fonction résultante que vous avez renvoyée peut être utilisée pour attacher plus de rappels à l'instance, mais ne spécifie pas strictement qu'une seule instance de ce type peut être créée. C'est tout l'intérêt d'un singleton.
Owen
6

J'ai obtenu cet exemple de JavaScript Patterns Build Better Applications with Coding and Design Patterns par le livre de Stoyan Stefanov au cas où vous auriez besoin d'une classe d'implémentation simple comme un objet singltone, vous pouvez utiliser la fonction immédiate comme suit:

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

Et vous pouvez vérifier cet exemple en suivant le cas de test:

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

Cette approche réussit tous les cas de test tandis que l'implémentation statique privée échouera lorsque l'extension du prototype est utilisée (elle peut être corrigée mais ce ne sera pas simple) et l'implémentation statique publique moins recommandée en raison de l'instance est exposée au public.

Démo jsFiddly.

Khamidulla
la source
5

Je pense avoir trouvé le moyen le plus propre de programmer en JavaScript, mais vous aurez besoin d'un peu d'imagination. J'ai eu cette idée d'une technique de travail dans le livre "javascript les bonnes pièces".

Au lieu d'utiliser le nouveau mot clé, vous pouvez créer une classe comme celle-ci:

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;
}

Vous pouvez instancier l'objet ci-dessus en disant:

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

Maintenant, avec cette méthode de travail à l'esprit, vous pouvez créer un singleton comme celui-ci:

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 };

}();

Vous pouvez maintenant obtenir votre instance en appelant

var obj = ClassSingleton.getInstance();

Je pense que c'est la meilleure façon car la "Classe" complète n'est même pas accessible.

David
la source
Mais avec cette technique, vous pouvez avoir plusieurs instances. Ce n'est pas correct.
nicolascolman
1
Je ne pense pas, vous ne pouvez même pas accéder à la classe sans passer par getInstance. Pourriez-vous élaborer?
David
David Maes Désolé mais je n'ai pas remarqué la validation dans le deuxième exemple. Je m'excuse.
nicolascolman
4

@CMS et @zzzzBov ont tous deux donné de merveilleuses réponses, mais juste pour ajouter ma propre interprétation basée sur mon passage au développement node.js lourd à partir de PHP / Zend Framework où les modèles singleton étaient courants.

Le code suivant, documenté par les commentaires, est basé sur les exigences suivantes:

  • une et une seule instance de l'objet fonction peut être instanciée
  • l'instance n'est pas accessible au public et n'est accessible que par une méthode publique
  • le constructeur n'est pas accessible au public et ne peut être instancié que si aucune instance n'est déjà disponible
  • la déclaration du constructeur doit permettre de modifier sa chaîne prototype. Cela permettra au constructeur d'hériter d'autres prototypes et d'offrir des méthodes "publiques" pour l'instance

Mon code est très similaire à celui de @ zzzzBov, sauf que j'ai ajouté une chaîne de prototype au constructeur et plus de commentaires qui devraient aider ceux qui viennent de PHP ou d'un langage similaire à traduire la POO traditionnelle en nature prototypique Javascripts. Ce n'est peut-être pas le "plus simple" mais je pense que c'est le plus approprié.

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

Notez que techniquement, la fonction anonyme auto-exécutable est elle-même un Singleton, comme le montre bien le code fourni par @CMS. Le seul hic ici, c'est qu'il n'est pas possible de modifier la chaîne prototype du constructeur lorsque le constructeur lui-même est anonyme.

Gardez à l'esprit que pour Javascript, les concepts de «public» et de «privé» ne s'appliquent pas comme ils le font en PHP ou Java. Mais nous avons obtenu le même effet en tirant parti des règles de Javascript sur la disponibilité de l'étendue fonctionnelle.

talentedmrjones
la source
Plusieurs instances peuvent être créées à partir de votre code:var a = Singleton.getInstance('foo'); var b = new a.constructor('bar');
zzzzBov
@zzzzBov: Je reçois juste des erreurs en essayant cela dans mon violon: jsfiddle.net/rxMu8
cincodenada
4

Je ne sais pas pourquoi personne n'a soulevé cette question, mais vous pouvez simplement faire:

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

  this.foo = function() {
    // whatever
  }
})()
Derek Chiang
la source
Cela semble être une bonne façon d'ignorer la méthode getInstance et d'obtenir une solution plus simple. Mais gardez à l'esprit que le singleton s'exécutera dès que le fichier sera analysé, ce qui signifie que les écouteurs DOM doivent être enveloppés dans une fonction $ (document) .ready
HoffZ
4

La réponse la plus claire devrait être celle-ci tirée du livre Learning JavaScript Design Patterns par Addy Osmani.

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

令狐 葱
la source
3

Je pense que c'est la manière la plus simple / la plus propre et la plus intuitive, bien qu'elle nécessite ES7:

export default class Singleton {

  static instance;

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

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

}

Le code source provient de: adam-bien.com

Alt Eisen
la source
Ceci est complètement faux et jetterait une erreur lors de l'appelnew Singleton()
UtkarshPramodGupta
2

Quel est le problème avec ça?

function Klass() {
   var instance = this;
   Klass = function () { return instance; }
}
Manav
la source
Test = Klass; t1 = new Test(); t2 = new Test();- aucune possibilité de renommer la classe ou de choisir un espace de noms différent.
zzzzBov
2

puis-je mettre mes 5 pièces. J'ai une fonction constructeur, ex.

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

Ce que je dois faire, c'est que chaque objet créé par ce CF sera le même.

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

tester

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

J'ai trouvé ce qui suit pour être le modèle Singleton plus facile, car en utilisant le nouvel opérateur rend ce immédiatement disponible au sein de la fonction, ce qui élimine la nécessité de retourner un littéral d'objet:

var singleton = new (function () {

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

singleton.printSomething();

marque
la source
2

Voici un exemple simple pour expliquer le motif singleton en javascript.

 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
la source
1

J'avais besoin de plusieurs singletons avec:

  • initialisation paresseuse
  • paramètres initiaux

et c'est donc ce que j'ai trouvé:

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 doit être Array pour que cela fonctionne, donc si vous avez des variables vides, passez simplement []

  • J'ai utilisé un objet fenêtre dans la fonction mais j'aurais pu passer un paramètre pour créer ma propre portée

  • Les paramètres de nom et de construction ne sont que des chaînes de caractères pour que la fenêtre [] fonctionne, mais avec une simple vérification de type, window.name et window.construct sont également possibles.

Fred
la source
1

Que diriez-vous de cette façon, assurez-vous simplement que la classe ne peut pas nouveau.

Par cela, vous pouvez utiliser l' instanceofop, vous pouvez également utiliser la chaîne de prototype pour hériter de la classe, c'est une classe régulière, mais ne pouvez pas la créer si vous voulez obtenir l'instance, utilisez simplementgetInstance

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

Si vous ne voulez pas exposer le instancemembre, mettez-le simplement dans une fermeture.

wener
la source
1

Voici l'extrait de ma présentation pour implémenter un modèle singleton. Cela m'est venu à l'esprit lors d'un processus d'entrevue et j'ai senti que je devrais saisir cela quelque part.

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

le même peut être trouvé sur ma page essentielle

curieux
la source
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
la source
1

N'est-ce pas aussi un 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
la source
1

Vous pouvez le faire avec des décorateurs comme dans cet exemple ci-dessous pour TypeScript:

class YourClass {

    @Singleton static singleton() {}

}

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

Ensuite, vous utilisez votre singleton comme ceci:

var myInstance = YourClass.singleton();

À ce jour, les décorateurs ne sont pas facilement disponibles dans les moteurs JavaScript. Vous devez vous assurer que votre runtime JavaScript a réellement des décorateurs activés ou utiliser des compilateurs comme Babel et TypeScript.

Notez également que l'instance singleton est créée "paresseuse", c'est-à-dire qu'elle n'est créée que lorsque vous l'utilisez pour la première fois.

Vad
la source
1

Modèle de module: dans "un style plus lisible". Vous pouvez voir facilement quelles méthodes sont publiques et lesquelles sont privées

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

vous pouvez maintenant utiliser des méthodes publiques comme

module.method2 (); // -> J'appelle une méthode privée sur une alerte de méthode publique ("hi im a private method")

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

ncubica
la source
1

Singleton:

Assurez-vous qu'une classe ne possède qu'une seule instance et fournissez un point d'accès global à celle-ci.

Le modèle Singleton limite le nombre d'instances d'un objet particulier à une seule. Cette instance unique est appelée le singleton.

  • définit getInstance () qui retourne l'instance unique.
  • responsable de la création et de la gestion de l'objet instance.

L'objet Singleton est implémenté comme une fonction anonyme immédiate. La fonction s'exécute immédiatement en l'enveloppant entre crochets suivi de deux crochets supplémentaires. Il est appelé anonyme car il n'a pas de nom.

Exemple de programme,

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
la source
1

Le plus simple / le plus propre pour moi signifie aussi simplement comprendre et pas de cloches et de sifflets comme on en discute beaucoup dans la version Java de la discussion:

Quel est un moyen efficace d'implémenter un modèle singleton en Java?

La réponse qui conviendrait le mieux à ce point de vue est la plus simple / la plus propre:

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

Et il ne peut être traduit qu'en partie en JavaScript. Certaines des différences dans Javascript sont:

  • les constructeurs ne peuvent pas être privés
  • Les classes ne peuvent pas avoir de champs déclarés

Mais étant donné la dernière syntaxe ECMA, il est possible de se rapprocher avec:

Modèle singleton comme exemple de classe JavaScript

 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;
  }
}

Exemple d'utilisation

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

Exemple de résultat

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

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

    app.name = name;

    return app;
}

Si vous êtes en cours:

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

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

        this.name = name;
    }
}

Tester:

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
la source
1

Si vous souhaitez utiliser des classes:

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

La sortie pour ce qui précède sera:

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

Une autre façon d'écrire Singleton en utilisant la fonction

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

La sortie pour ce qui précède sera:

console.log(a.name,a.age,b.name,b.age)// s 1 s 1
sudhee
la source