Il modo più semplice / pulito per implementare singleton in JavaScript?


300

Qual è il modo più semplice / pulito per implementare il modello singleton in JavaScript?


15
Votazione per la risposta accettata non essendo affatto un singleton. È solo una variabile globale.
mlibby,

5
Questa è una tonnellata di informazioni, ma illustra davvero le differenze tra i diversi modelli di design JS. Mi ha aiutato molto: addyosmani.com/resources/essentialjsdesignpatterns/book
Justin

Risposte:


315

Penso che il modo più semplice sia dichiarare un semplice oggetto letterale:

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

Se vuoi membri privati ​​nella tua istanza singleton, puoi fare qualcosa del genere:

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

  function privateMethod () {
    // ...
  }

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

Questo è stato chiamato il modello del modulo , in pratica consente di incapsulare membri privati ​​su un oggetto, sfruttando l'utilizzo di chiusure .

AGGIORNAMENTO: Vorrei aggiungere che se si desidera impedire la modifica dell'oggetto singleton, è possibile bloccarlo utilizzando ES5Object.freeze metodo .

Ciò renderà l'oggetto immutabile, impedendo qualsiasi modifica alla sua struttura e ai suoi valori.

Inoltre, vorrei menzionare che se si utilizza ES6, è possibile rappresentare un singleton utilizzando i moduli ES molto facilmente e si può persino mantenere lo stato privato dichiarando le variabili nell'ambito del modulo :

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

function privateFn () {
  // ...
}

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

Quindi puoi semplicemente importare l'oggetto singleton per usarlo:

import myInstance from './my-singleton.js'
// ...

46
+1 Non è un po 'strano cercare un "modello Singleton" in una lingua con variabili globali ???
Victor,

4
Utilizzando il modello del modulo, come potrebbe un membro pubblico accedere a un altro membro pubblico? Cioè, come publicMethod1chiamerebbe publicMethod2?
tipo del

4
@Tom, sì, lo schema è nato su linguaggi OOP basati su classi-Ricordo molte implementazioni che includevano un getInstancemetodo statico e un costrutto privato-, ma IMO, questo è il modo più "semplice" per costruire un oggetto singleton in Javascript, e alla fine soddisfa lo stesso scopo -un singolo oggetto, che non puoi inizializzare di nuovo (non c'è un costruttore, è solo un oggetto) -. Circa il codice si è collegato, ha alcuni problemi, scambiare le ae bvariabili dichiarazioni e di prova a === window. Saluti.
CMS,

15
@Victor - Non è strano cercare un "modello singleton" in tale linguaggio. molti linguaggi orientati agli oggetti utilizzano variabili globali e sono ancora in uso singleton. Singleton non garantisce solo che ci sarà un solo oggetto di una determinata classe. Singleton ha alcune altre funzionalità: 1) dovrebbe essere inizializzato al primo utilizzo (il che non significa solo un'inizializzazione ritardata, ma garantisce anche che l'oggetto sia davvero pronto per essere utilizzato) 2) dovrebbe essere thread-safe. Il pattern del modulo può essere sostitutivo del pattern singleton ma solo nel browser (e non sempre).
skalee,

55
Questa non dovrebbe essere la risposta accettata. Questo non è affatto un singleton! Questa è solo una variabile globale. C'è un mondo di differenza tra i due.
mlibby,

172

Penso che l'approccio più pulito sia qualcosa di simile:

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

Successivamente, è possibile richiamare la funzione come

var test = SingletonFactory.getInstance();

4
Nota: il costruttore originale può essere letto nuovamente usando delete instance.constructor:x = SingletonClass.getInstance();delete x.constructor;new x.constructor;
Rob W

var test = SingletonClass.getInstance () - non sembra molto pulito o simile a JS. Altre animezioni che finiscono con a = new Foo (); b = new Foo (); a === b // true
Matthias,

3
Odora di più come una fabbrica per me con l'intera parte "getInstance".
Lajos Meszaros,

1
Questo non è un singleton perché puoi crearne più istanze con Object.create.
AndroidDev

Il violino è rotto
HoffZ,

103

Non sono sicuro di essere d'accordo con il modello di modulo utilizzato in sostituzione di un modello singleton. Ho visto spesso i singleton usati e maltrattati in luoghi in cui sono del tutto superflui, e sono sicuro che il pattern del modulo colma molte lacune in cui i programmatori userebbero un singleton, tuttavia il pattern del modulo non è un singleton.

modello del modulo:

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

Tutto ciò che è inizializzato nel modello del modulo accade quando Foo viene dichiarato. Inoltre, il modello di modulo può essere utilizzato per inizializzare un costruttore, che può quindi essere istanziato più volte. Mentre il modello del modulo è lo strumento giusto per molti lavori, non è equivalente a un singleton.

modello singleton:

forma breve
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();
}
forma lunga, usando il modello del modulo
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 entrambe le versioni del modello Singleton che ho fornito, il costruttore stesso può essere utilizzato come accessorio:

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

Se non ti senti a tuo agio nell'usare il costruttore in questo modo, puoi lanciare un errore if (instance)nell'istruzione e continuare ad usare la forma lunga:

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

Dovrei anche menzionare che il modello singleton si adatta bene al modello di funzione del costruttore implicito:

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

4
Non ho mai detto nulla dei singoli come una cattiva o buona idea. Ho detto che la tua implementazione di singleton è molto più complicata di quanto debba essere perché confondi i limiti di Java con il modello come se non lo capissi affatto. È come implementare un modello di strategia creando funzioni e metodi di costruzione quando puoi semplicemente usare funzioni anonime per quello in Javascript.
Esailija,

11
@Esailija, mi sembra che tu non capisca il modello singleton. Il modello singleton è un modello di progettazione che limita l'istanziazione di una classe a un oggetto. var singleton = {}non corrisponde a tale definizione.
zzzzBov,

9
Questo vale solo per linguaggi come Java a causa delle loro limitazioni. var singleton = {}è come si implementa singleton in Javascript .
Esailija,

2
@Esailija, "In un linguaggio di programmazione basato su prototipo, in cui vengono utilizzati oggetti ma non classi ..." JavaScript ha il concetto di classi, quindi non si applica.
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

1
Il congelamento avrebbe senso se Singleton fosse un wrapper in qualche altra classe e avesse solo il instancecampo. Come è attualmente ( instanceimpostato su this) questa classe potrebbe avere anche altri campi e il congelamento non ha senso imo.
thisismydesign

10

Quanto segue funziona nel nodo v6

class Foo {
  constructor(msg) {

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

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

Testiamo:

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

8

In ES6 il modo giusto per farlo è:

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

Oppure, se non desideri che venga generato un errore durante la creazione della seconda istanza, puoi semplicemente restituire l'ultima istanza, in questo modo:

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"


Ciao, puoi aiutarmi a conoscere la differenza tra _istanza e istanza mentre stavo usando istanza e il codice non funzionava
Abhinav bhardwaj

Non c'è alcuna differenza tecnica in instancee _instance. È solo una convenzione di denominazione nei linguaggi di programmazione che chiamiamo variabili private precedute da un trattino basso. Sospetto che il motivo per cui il tuo codice non funziona è che stai utilizzando this.instanceinvece diMyClass.instance
UtkarshPramodGupta

7

Esistono più modi per scuoiare un gatto :) A seconda dei tuoi gusti o delle tue esigenze specifiche puoi applicare una qualsiasi delle soluzioni proposte. Personalmente cerco la prima soluzione di CMS ogni volta che è possibile (quando non hai bisogno di privacy). Poiché la domanda riguardava il più semplice e il più pulito, quello è il vincitore. O anche:

var myInstance = {}; // done!

Questo (citazione dal mio blog) ...

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

non ha molto senso (il mio esempio di blog non lo fa neanche) perché non ha bisogno di alcun parametro privato, quindi è praticamente lo stesso di:

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

Il frammento di codice 2 contiene un errore di sintassi. Non puoi scriverethis.f(){}
xoxox il

7

Deprezzo la mia risposta, vedi la mia altra .

Di solito il modello di modulo (vedi la risposta di CMS) che NON è un modello singleton è abbastanza buono. Tuttavia una delle caratteristiche di singleton è che la sua inizializzazione è ritardata fino a quando non è necessario l'oggetto. Il modello del modulo non ha questa funzione.

La mia proposta (CoffeeScript):

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

Che ha compilato questo in JavaScript:

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

Quindi posso fare quanto segue:

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

Perché dovresti caricare il modulo se non è necessario? E quando è necessario caricare il modulo, quindi si carica il modulo e si inizializza.
Esailija,

6

Risposta breve:

A causa della natura non bloccante di JavaScript, i Singleton in JavaScript sono davvero brutti in uso. Le variabili globali ti daranno un'istanza anche attraverso l'intera applicazione senza tutti questi callback, il modello di modulo nasconde delicatamente gli interni dietro l'interfaccia. Vedi la risposta di @CMS.

Ma, dal momento che volevi 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);
    }
  };

};

Uso:

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

Spiegazione:

I singleton offrono più di una sola istanza nell'intera applicazione: la loro inizializzazione è ritardata fino al primo utilizzo. Questa è davvero una grande cosa quando si tratta di oggetti la cui inizializzazione è costosa. Costoso di solito significa I / O e in JavaScript I / O significa sempre richiamate.

Non fidarti delle risposte che ti danno un'interfaccia simile instance = singleton.getInstance(), tutti mancano il punto.

Se non accettano l'esecuzione del callback quando l'istanza è pronta, non funzioneranno quando l'inizializzatore esegue l'I / O.

Sì, i callback sembrano sempre più brutti della chiamata di funzione che restituisce immediatamente l'istanza dell'oggetto. Ma ancora: quando si esegue l'I / O, i callback sono obbligatori. Se non si desidera eseguire alcun I / O, l'istanza è abbastanza economica per farlo all'avvio del programma.

Esempio 1, inizializzatore economico:

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

Esempio 2, inizializzazione con I / O:

In questo esempio viene setTimeouteseguita un'operazione I / O costosa. Questo spiega perché i singoli in JavaScript hanno davvero bisogno di callback.

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

Attraverso prove e tribolazioni con altre risposte singleton che non lo hanno tagliato, sono arrivato al codice risultante notevolmente simile a questo.
mheyman,

Per una ragione o per l'altra, questa è l'unica risposta che ha senso per me. Le altre risposte mi ricordano tutte l'episodio del goon show in cui tre uomini cercano di arrampicarsi su un muro alto quattro persone, arrampicandosi sulle spalle in modo ricorsivo.
Tim Ogilvy,

Il calback stacking è la cosa di cui avevo veramente bisogno! Grazie!!
Tim Ogilvy,

Questo approccio non ti dà mai un singleton come: singleton (singletonInitializer)! == singleton (singletonInitializer) sono due istanze diverse. La funzione risultante restituita può essere utilizzata per allegare più callback all'istanza, ma non specifica rigorosamente che è possibile creare solo un'istanza di questo tipo. Qual è il punto centrale di un singleton.
Owen,

6

Ho preso questo esempio da JavaScript Patterns Build Better Applications with Coding and Design Patterns Dal libro di Stoyan Stefanov nel caso in cui abbiate bisogno di una semplice classe di implementazione come l'oggetto singltone, potete usare la funzione immediata come segue:

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

E puoi controllare questo esempio seguendo il test case:

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

Questo approccio supera tutti i casi di test mentre l'implementazione statica privata fallirà quando viene utilizzata l'estensione del prototipo (può essere riparata ma non sarà semplice) e l'implementazione statica pubblica meno consigliabile a causa dell'istanza è esposta al pubblico.

jsFiddly demo.


5

Penso di aver trovato il modo più pulito per programmare in JavaScript, ma avrai bisogno di un po 'di immaginazione. Ho avuto questa idea da una tecnica di lavoro nel libro "javascript the good parts".

Invece di utilizzare la nuova parola chiave, è possibile creare una classe come questa:

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

È possibile creare un'istanza dell'oggetto sopra dicendo:

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

Ora con questo metodo di lavoro in mente potresti creare un singleton come questo:

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

}();

Ora puoi ottenere la tua istanza chiamando

var obj = ClassSingleton.getInstance();

Penso che questo sia il modo più accurato in quanto la "Classe" completa non è nemmeno accessibile.


Ma con questa tecnica potresti avere più di un'istanza. Non è giusto
nicolascolman,

1
Non credo, non puoi nemmeno accedere alla classe senza passare attraverso getInstance. Potresti elaborare?
David,

David Maes Scusate ma non ho notato la convalida nel secondo esempio. Chiedo scusa.
nicolascolman,

4

@CMS e @zzzzBov hanno dato entrambe risposte meravigliose, ma solo per aggiungere la mia interpretazione basata sul fatto che mi sono trasferito nello sviluppo di node.js pesante da PHP / Zend Framework in cui i modelli singleton erano comuni.

Il seguente codice documentato da commenti si basa sui seguenti requisiti:

  • è possibile creare un'istanza di una sola istanza dell'oggetto funzione
  • l'istanza non è pubblicamente disponibile e è possibile accedervi solo tramite un metodo pubblico
  • il costruttore non è pubblicamente disponibile e può essere istanziato solo se non è già disponibile un'istanza
  • la dichiarazione del costruttore deve consentire la modifica della sua catena di prototipi. Ciò consentirà al costruttore di ereditare da altri prototipi e offrirà metodi "pubblici" per l'istanza

Il mio codice è molto simile a quello di @ zzzzBov, tranne per il fatto che ho aggiunto una catena di prototipi al costruttore e altri commenti che dovrebbero aiutare coloro che provengono da PHP o un linguaggio simile a tradurre OOP tradizionale in natura prototipica di Javascripts. Potrebbe non essere il "più semplice" ma credo che sia il più corretto.

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

Si noti che tecnicamente, la funzione anonima autoeseguente è essa stessa un Singleton, come ben dimostrato nel codice fornito da @CMS. L'unico problema è che non è possibile modificare la catena di prototipi del costruttore quando il costruttore stesso è anonimo.

Tieni presente che per Javascript, i concetti di "pubblico" e "privato" non si applicano come in PHP o Java. Ma abbiamo ottenuto lo stesso effetto sfruttando le regole di Javascript relative alla disponibilità dell'ambito funzionale.


Più istanze possono essere create dal tuo codice:var a = Singleton.getInstance('foo'); var b = new a.constructor('bar');
zzzzBov

@zzzzBov: sto solo riscontrando errori nel provare il mio violino: jsfiddle.net/rxMu8
cincodenada

4

Non sono sicuro del motivo per cui nessuno ha parlato di questo, ma potresti semplicemente fare:

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

  this.foo = function() {
    // whatever
  }
})()

Questo sembra essere un modo pulito per saltare il metodo getInstance e ottenere una soluzione più semplice. Ma tieni presente che il singleton verrà eseguito non appena il file viene analizzato, il che significa che i listener DOM devono essere racchiusi in una funzione $ (documento). Già
HoffZ

4

La risposta più chiara dovrebbe essere questa dal libro Learning JavaScript Design Patterns di 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;
    }
 
  };
 
})();


3

Credo che questo sia il modo più semplice / pulito e intuitivo anche se richiede ES7:

export default class Singleton {

  static instance;

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

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

}

Il codice sorgente proviene da: adam-bien.com


Questo è completamente sbagliato e genererebbe un errore nel chiamarenew Singleton()
UtkarshPramodGupta

2

Cosa c'è di sbagliato in questo?

function Klass() {
   var instance = this;
   Klass = function () { return instance; }
}

Test = Klass; t1 = new Test(); t2 = new Test();- nessuna possibilità di rinominare la classe o scegliere uno spazio dei nomi diverso.
zzzzBov,

2

posso mettere le mie 5 monete. Ho una funzione di costruzione, ad es.

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

Quello che devo fare è che ogni oggetto creato da questo CF sarà lo stesso.

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

test

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

2

Ho trovato il seguente per essere il pattern Singleton più facile, perché con il nuovo operatore rende questo immediatamente disponibili all'interno della funzione, eliminando la necessità di restituire un oggetto letterale:

var singleton = new (function () {

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

singleton.printSomething();


2

Ecco il semplice esempio per spiegare il modello singleton in 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();

1

Avevo bisogno di diversi singoli con:

  • inizializzazione pigra
  • parametri iniziali

e quindi questo è quello che mi è venuto in mente:

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 deve essere Array affinché funzioni, quindi se hai variabili vuote, passa []

  • Ho usato l'oggetto finestra nella funzione ma avrei potuto passare un parametro per creare il mio ambito

  • i parametri nome e costrutto sono solo String per il funzionamento di window [] ma con alcuni semplici controlli del tipo, sono anche possibili window.name e window.construct.


1

Che ne dici di questo, assicurati che la classe non possa più essere nuova.

Con questo, puoi usare anche l' instanceofop, puoi usare la catena di prototipi per ereditare la classe, è una classe normale, ma non puoi rinnovarla, se vuoi ottenere l'istanza basta usaregetInstance

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

Se non vuoi esporre il instancemembro, mettilo in una chiusura.


1

Di seguito è riportato il frammento della mia passeggiata attraverso per implementare un modello singleton. Questo mi è venuto in mente durante un processo di intervista e ho sentito che avrei dovuto catturarlo da qualche parte.

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

lo stesso può essere trovato sul mio succo pagina


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

1

Anche questo non è 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();

1

Puoi farlo con decoratori come nell'esempio seguente per TypeScript:

class YourClass {

    @Singleton static singleton() {}

}

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

Quindi usi il tuo singleton in questo modo:

var myInstance = YourClass.singleton();

Al momento della stesura di questo documento, i decoratori non sono prontamente disponibili nei motori JavaScript. Dovresti assicurarti che il runtime JavaScript abbia effettivamente i decoratori abilitati o utilizzare compilatori come Babel e TypeScript.

Si noti inoltre che l'istanza singleton viene creata "pigra", ovvero viene creata solo quando la si utilizza per la prima volta.


1

Schema del modulo: in "stile più leggibile". Puoi vedere facilmente quali metodi sono pubblici e quali sono privati

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

ora puoi usare metodi pubblici come

module.method2 (); // -> Sto chiamando un metodo privato tramite un avviso di metodo pubblico ("ciao im un metodo privato")

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


1

Singleton:

Assicurati che una classe abbia solo un'istanza e fornisci un punto di accesso globale ad essa.

Il modello Singleton limita il numero di istanze di un particolare oggetto a una sola. Questa singola istanza è chiamata singleton.

  • definisce getInstance () che restituisce l'istanza univoca.
  • responsabile della creazione e della gestione dell'oggetto dell'istanza.

L'oggetto Singleton è implementato come una funzione anonima immediata. La funzione viene eseguita immediatamente racchiudendola tra parentesi seguita da due parentesi aggiuntive. Si chiama anonimo perché non ha un nome.

Programma di esempio,

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


1

Più semplice / più pulito per me significa anche semplicemente capire e senza campane e fischietti come sono molto discussi nella versione Java della discussione:

Qual è un modo efficiente per implementare un modello singleton in Java?

Dal mio punto di vista la risposta che si adatterebbe meglio al più semplice / pulito è:

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

E può essere tradotto solo parzialmente in JavaScript. Alcune delle differenze in Javascript sono:

  • i costruttori non possono essere privati
  • Le classi non possono avere campi dichiarati

Ma data la sintassi ECMA più recente, è possibile avvicinarsi a:

Modello Singleton come esempio di 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;
  }
}

Esempio di utilizzo

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

Risultato di esempio

DefaultField1
DefaultField2

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

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

    app.name = name;

    return app;
}

Se sei in classe:

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

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

        this.name = name;
    }
}

Test:

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

1

Se vuoi usare le classi:

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

L'output per quanto sopra sarà:

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

Un altro modo di scrivere Singleton usando la funzione

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

L'output per quanto sopra sarà:

console.log(a.name,a.age,b.name,b.age)// s 1 s 1
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.