¿La forma más simple / limpia de implementar singleton en JavaScript?

Respuestas:

315

Creo que la forma más fácil es declarar un objeto literal simple:

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

Si desea miembros privados en su instancia de singleton, puede hacer algo como esto:

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

  function privateMethod () {
    // ...
  }

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

Esto ha sido llamado el patrón de módulo , que básicamente le permite encapsular los miembros privados en un objeto, mediante el aprovechamiento de la utilización de cierres .

ACTUALIZACIÓN: Me gustaría agregar que si desea evitar la modificación del objeto singleton, puede congelarlo , utilizando el Object.freezemétodo ES5 .

Eso hará que el objeto sea inmutable, evitando cualquier modificación en su estructura y valores.

Además, me gustaría mencionar que si está utilizando ES6, puede representar un singleton utilizando módulos ES muy fácilmente, e incluso puede mantener el estado privado declarando variables en el alcance del módulo :

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

function privateFn () {
  // ...
}

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

Luego, simplemente puede importar el objeto singleton para usarlo:

import myInstance from './my-singleton.js'
// ...
CMS
fuente
46
+1 ¿No es un poco extraño buscar un "patrón Singleton" en un lenguaje con variables globales?
Victor
44
Usando el patrón del módulo, ¿cómo accedería un miembro público a otro miembro público? Es decir, ¿cómo publicMethod1llamaría publicMethod2?
typeof
44
@Tom, sí, el patrón nació en lenguajes OOP basados ​​en clases -Recuerdo muchas implementaciones que involucraban un getInstancemétodo estático y un constructor privado-, pero en mi opinión, esta es la forma más "simple" de construir un objeto singleton en Javascript, y al final cumple el mismo propósito -un objeto único, que no puede inicializar nuevamente (no hay constructor, es solo un objeto) -. Sobre el código se ha vinculado, tiene algunos problemas, intercambiar los ay blas declaraciones de variables y prueba a === window. Salud.
CMS
15
@Victor: no es extraño buscar un "patrón singleton" en dicho lenguaje. muchos lenguajes orientados a objetos utilizan variables globales y todavía se usan singletons. Singleton no solo garantiza que habrá un solo objeto de una clase dada. Singleton tiene algunas características más: 1) debe inicializarse en el primer uso (lo que no solo significa una inicialización retrasada, sino que también garantiza que el objeto esté realmente listo para usarse) 2) debe ser seguro para subprocesos. El patrón del módulo puede reemplazar al patrón singleton pero solo en el navegador (y no siempre).
skalee
55
Esta no debería ser la respuesta aceptada. ¡Esto no es un singleton en absoluto! Esto es solo una variable global. Hay un mundo de diferencia entre los dos.
mlibby
172

Creo que el enfoque más limpio es algo como:

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

Luego, puede invocar la función como

var test = SingletonFactory.getInstance();
sebarmeli
fuente
44
Observación: El constructor original se puede leer nuevamente usando delete instance.constructor:x = SingletonClass.getInstance();delete x.constructor;new x.constructor;
Rob W
var test = SingletonClass.getInstance () - no se ve muy limpio o JS gusta. Otras almas que terminan con a = new Foo (); b = nuevo Foo (); a === b // verdadero
Matthias
3
Me huele más a fábrica con toda la parte "getInstance".
Lajos Meszaros
1
Este no es un singleton porque puede crear múltiples instancias con Object.create.
AndroidDev
Fiddle está roto
HoffZ
103

No estoy seguro de estar de acuerdo con el patrón del módulo que se utiliza como reemplazo de un patrón singleton. A menudo he visto los singletons utilizados y abusados ​​en lugares donde son totalmente innecesarios, y estoy seguro de que el patrón del módulo llena muchos vacíos donde los programadores usarían un singleton, sin embargo, el patrón del módulo no es un singleton.

patrón de módulo:

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

Todo lo que se inicializa en el patrón del módulo ocurre cuando Foose declara. Además, el patrón del módulo se puede usar para inicializar un constructor, que luego se puede instanciar varias veces. Si bien el patrón del módulo es la herramienta adecuada para muchos trabajos, no es equivalente a un singleton.

patrón singleton:

forma corta
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 larga, usando patrón de módulo
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;
}());

En ambas versiones del patrón Singleton que he proporcionado, el propio constructor puede usarse como el descriptor de acceso:

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

Si no se siente cómodo usando el constructor de esta manera, puede arrojar un error en la if (instance)declaración y seguir usando la forma larga:

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

También debo mencionar que el patrón singleton encaja bien con el patrón de función de constructor implícito:

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
fuente
44
Nunca dije nada sobre los singletons como una mala o buena idea. Dije que su implementación de singleton es mucho más complicada de lo que debe ser porque confunde las limitaciones de Java con el patrón como si no lo entendiera en absoluto. Es como implementar un patrón de estrategia haciendo funciones y métodos de constructor cuando simplemente puedes usar funciones anónimas para eso en Javascript.
Esailija
11
@Esailija, me parece que no entiendes el patrón singleton. El patrón singleton es un patrón de diseño que restringe la instanciación de una clase a un objeto. var singleton = {}no se ajusta a esa definición.
zzzzBov
99
Eso solo se aplica a lenguajes como Java debido a sus limitaciones. var singleton = {}es cómo implementar singleton en Javascript .
Esailija
2
@Esailija, "En un lenguaje de programación basado en prototipos, donde se usan objetos pero no clases ..." JavaScript tiene el concepto de clases, por lo que no se aplica.
zzzzBov
18

En 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
fuente
1
La congelación tendría sentido si Singleton fuera una envoltura alrededor de otra clase y solo tuviera el instancecampo. Como está actualmente ( instanceconfigurado en this), esta clase también puede tener otros campos y congelar no tiene sentido.
thisismydesign
10

Lo siguiente funciona en el nodo v6

class Foo {
  constructor(msg) {

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

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

Probamos:

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

En ES6, la forma correcta de hacer esto es:

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

O, si no desea que se genere un error en la creación de la segunda instancia, simplemente puede devolver la última instancia, de esta manera:

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
fuente
Hola, ¿pueden ayudarme a saber la diferencia entre _instancia e instancia, ya que estaba usando instancia y el código no funcionaba?
Abhinav bhardwaj
No hay diferencia técnica en instancey _instance. Es solo una convención de nomenclatura en lenguajes de programación que nombramos variables privadas antepuestas con un guión bajo. Sospecho que la razón de que su código no funcione es que está usando en this.instancelugar deMyClass.instance
UtkarshPramodGupta
7

Hay más de una forma de pelar un gato :) Dependiendo de su gusto o necesidad específica, puede aplicar cualquiera de las soluciones propuestas. Yo personalmente busco la primera solución de CMS siempre que sea posible (cuando no necesita privacidad). Como la pregunta era sobre la más simple y limpia, esa es la ganadora. O incluso:

var myInstance = {}; // done!

Esto (cita de mi blog) ...

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

no tiene mucho sentido (mi ejemplo de blog tampoco) porque no necesita ninguna variable privada, por lo que es casi lo mismo que:

var SingletonClass = { 
    myFunction: function () { 
        //do stuff 
    },
    instance: 1 
}
Stoyan
fuente
El fragmento de código 2 contiene un error de sintaxis. No puedes escribirthis.f(){}
xoxox
7

Deprecio mi respuesta, veo la otra .

Por lo general, el patrón de módulo (consulte la respuesta de CMS) que NO es un patrón único es lo suficientemente bueno. Sin embargo, una de las características de singleton es que su inicialización se retrasa hasta que se necesita un objeto. El patrón del módulo carece de esta característica.

Mi propuesta (CoffeeScript):

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

Que compiló esto en JavaScript:

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

Entonces puedo hacer lo siguiente:

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
fuente
¿Por qué cargarías el módulo si no es necesario? Y cuando necesita cargar el módulo, carga el módulo y se inicializa.
Esailija
6

Respuesta corta:

Debido a la naturaleza no bloqueante de JavaScript, los Singletons en JavaScript son realmente feos en uso. Las variables globales también le darán una instancia a través de toda la aplicación sin todas estas devoluciones de llamada, el patrón del módulo oculta suavemente las partes internas detrás de la interfaz. Ver la respuesta de @CMS.

Pero, como querías 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();
});

Explicación:

Los Singletons le brindan más de una instancia a través de toda la aplicación: su inicialización se retrasa hasta el primer uso. Esto es realmente importante cuando se trata de objetos cuya inicialización es costosa. Caro generalmente significa E / S y en JavaScript E / S siempre significa devoluciones de llamada.

No confíes en las respuestas que te dan una interfaz como instance = singleton.getInstance(), todas pierden el punto.

Si no toman la devolución de llamada para ejecutarse cuando la instancia está lista, entonces no funcionarán cuando el inicializador haga E / S.

Sí, las devoluciones de llamada siempre se ven más feas que la llamada a la función, que devuelve inmediatamente la instancia del objeto. Pero de nuevo: cuando haces E / S, las devoluciones de llamada son obligatorias. Si no desea hacer ninguna E / S, la creación de instancias es lo suficientemente barata como para hacerlo al inicio del programa.

Ejemplo 1, inicializador barato:

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

Ejemplo 2, inicialización con E / S:

En este ejemplo, setTimeoutsimula algunas operaciones costosas de E / S. Esto ilustra por qué los singletons en JavaScript realmente necesitan devoluciones de llamada.

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
fuente
A través de pruebas y tribulaciones con otras respuestas únicas que no lo cortaron, llegué al código resultante notablemente similar a este.
mheyman
Por una razón u otra, esta es la única respuesta que tiene sentido para mí. Todas las otras respuestas me recuerdan el episodio del show de matones en el que tres hombres intentan escalar una pared con cuatro personas de altura, trepando sobre los hombros de forma recursiva.
Tim Ogilvy
¡El apilamiento de calback es lo que realmente necesitaba! ¡¡Gracias!!
Tim Ogilvy
Este enfoque nunca te da un singleton como: singleton (singletonInitializer)! == singleton (singletonInitializer) son dos instancias diferentes. La función resultante que devolvió se puede usar para adjuntar más devoluciones de llamada a la instancia, pero no especifica estrictamente que solo se pueda crear una instancia de este tipo. Cuál es el objetivo de un singleton.
Owen
6

Obtuve este ejemplo de Patrones de JavaScript Construir mejores aplicaciones con codificación y patrones de diseño Por el libro de Stoyan Stefanov en caso de que necesite alguna clase de implementación simple como un objeto singltone, puede usar la función inmediata de la siguiente manera:

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

Y puede consultar este ejemplo siguiendo el caso de prueba:

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

Este enfoque supera todos los casos de prueba, mientras que la implementación estática privada fallará cuando se use la extensión del prototipo (se puede arreglar, pero no será simple) y la implementación estática pública es menos aconsejable debido a que la instancia está expuesta al público.

jsFiddly demo.

Khamidulla
fuente
5

Creo que he encontrado la forma más limpia de programar en JavaScript, pero necesitarás algo de imaginación. Obtuve esta idea de una técnica de trabajo en el libro "javascript las partes buenas".

En lugar de usar la nueva palabra clave, puede crear una clase como esta:

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

Puede crear una instancia del objeto anterior diciendo:

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

Ahora, con este método de trabajo en mente, podría crear un singleton como este:

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

}();

Ahora puede obtener su instancia llamando

var obj = ClassSingleton.getInstance();

Creo que esta es la mejor manera ya que ni siquiera se puede acceder a la "Clase" completa.

David
fuente
Pero con esta técnica podrías tener más de una instancia. Eso no está bien.
nicolascolman
1
No lo creo, ni siquiera puedes acceder a la clase sin pasar por getInstance. ¿Podrías dar más detalles?
David
David Maes Lo siento pero no noté la validación en el segundo ejemplo. Me disculpo.
nicolascolman
4

@CMS y @zzzzBov han dado respuestas maravillosas, pero solo para agregar mi propia interpretación basada en que me mudé al desarrollo de node.js pesado desde PHP / Zend Framework, donde los patrones singleton eran comunes.

El siguiente código documentado con comentarios se basa en los siguientes requisitos:

  • se puede instanciar una y solo una instancia del objeto de función
  • la instancia no está disponible públicamente y solo se puede acceder a través de un método público
  • el constructor no está disponible públicamente y solo se puede crear una instancia si aún no hay una instancia disponible
  • La declaración del constructor debe permitir que se modifique su cadena prototipo. Esto permitirá que el constructor herede de otros prototipos y ofrecerá métodos "públicos" para la instancia.

Mi código es muy similar al de @ zzzzBov, excepto que he agregado una cadena de prototipo al constructor y más comentarios que deberían ayudar a aquellos que provienen de PHP o un lenguaje similar a traducir OOP tradicional a la naturaleza prototípica de Javascripts. Puede que no sea el "más simple" pero creo que es el más apropiado.

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

Tenga en cuenta que, técnicamente, la función anónima de ejecución automática es en sí misma un Singleton, como se demuestra muy bien en el código proporcionado por @CMS. El único inconveniente aquí es que no es posible modificar la cadena de prototipo del constructor cuando el constructor en sí es anónimo.

Tenga en cuenta que para Javascript, los conceptos de "público" y "privado" no se aplican como lo hacen en PHP o Java. Pero hemos logrado el mismo efecto al aprovechar las reglas de disponibilidad de alcance funcional de Javascript.

talentosojones
fuente
Se pueden crear varias instancias a partir de su código:var a = Singleton.getInstance('foo'); var b = new a.constructor('bar');
zzzzBov
@zzzzBov: Estoy recibiendo errores al intentar eso en mi violín: jsfiddle.net/rxMu8
cincodenada
4

No estoy seguro de por qué nadie mencionó esto, pero podrías hacer lo siguiente:

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

  this.foo = function() {
    // whatever
  }
})()
Derek Chiang
fuente
Esta parece ser una buena forma de omitir el método getInstance y obtener una solución más simple. Pero tenga en cuenta que el singleton se ejecutará tan pronto como se analice el archivo, lo que significa que los oyentes DOM deben estar envueltos en una función $ (document) .ready
HoffZ
4

La respuesta más clara debería ser esta del libro Learning JavaScript Design Patterns de 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;
    }
 
  };
 
})();

令狐 葱
fuente
3

Creo que esta es la forma más simple / limpia e intuitiva, aunque requiere ES7:

export default class Singleton {

  static instance;

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

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

}

El código fuente es de: adam-bien.com

Alt Eisen
fuente
Esto es completamente incorrecto y arrojaría un error al llamarnew Singleton()
UtkarshPramodGupta
2

¿Qué tiene de malo esto?

function Klass() {
   var instance = this;
   Klass = function () { return instance; }
}
Manav
fuente
Test = Klass; t1 = new Test(); t2 = new Test();- no hay oportunidad de cambiar el nombre de la clase o elegir un espacio de nombres diferente.
zzzzBov
2

¿Puedo poner mis 5 monedas? Tengo una función constructora, ej.

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

Lo que necesito hacer es que cada objeto creado por este CF será el mismo.

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

prueba

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

He encontrado que lo siguiente es el patrón más fácil Singleton, porque el uso de la nueva operador hace que esta inmediatamente disponible dentro de la función, lo que elimina la necesidad de devolver un objeto literal:

var singleton = new (function () {

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

singleton.printSomething();

marca
fuente
2

Aquí está el ejemplo simple para explicar el patrón 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
fuente
1

Necesitaba varios singletons con:

  • inicialización perezosa
  • parámetros iniciales

y esto fue lo que se me ocurrió:

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;
}
  • los argumentos deben ser Array para que esto funcione, así que si tiene variables vacías, simplemente pase []

  • Utilicé un objeto de ventana en la función, pero podría haber pasado un parámetro para crear mi propio alcance

  • los parámetros de nombre y construcción son solo String para que la ventana [] funcione, pero con algunas verificaciones de tipo simples, window.name y window.construct también son posibles.

Fred
fuente
1

¿Qué tal esta manera? Solo asegúrate de que la clase no pueda volver a empezar.

Con esto, puede usar el instanceofop, también, puede usar la cadena de prototipo para heredar la clase, es una clase regular, pero no puede ser nueva, si desea obtener la instancia simplemente usegetInstance

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 no desea exponer al instancemiembro, solo ciérrelo.

Wener
fuente
1

El siguiente es el fragmento de mi recorrido para implementar un patrón singleton. Esto se me ocurrió durante un proceso de entrevista y sentí que debería capturar esto en alguna 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 mismo se puede conocer en mi GIST página

curioso
fuente
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
fuente
1

¿No es esto un singleton también?

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
fuente
1

Puede hacerlo con decoradores como en este ejemplo a continuación para TypeScript:

class YourClass {

    @Singleton static singleton() {}

}

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

Luego usas tu singleton así:

var myInstance = YourClass.singleton();

Al escribir estas líneas, los decoradores no están disponibles en los motores de JavaScript. Debería asegurarse de que su tiempo de ejecución de JavaScript tenga decoradores realmente habilitados o usar compiladores como Babel y TypeScript.

También tenga en cuenta que la instancia de singleton se crea "perezosa", es decir, se crea solo cuando la usa por primera vez.

Vad
fuente
1

Patrón de módulo: en "estilo más legible". Puede ver fácilmente qué métodos son públicos y cuáles son privados

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

ahora puedes usar métodos públicos como

module.method2 (); // -> Estoy llamando a un método privado a través de una alerta de método público ("hola soy un método privado")

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

ncubica
fuente
1

Único:

Asegúrese de que una clase tenga solo una instancia y proporcione un punto de acceso global.

El patrón Singleton limita el número de instancias de un objeto en particular a solo uno. Esta instancia única se llama singleton.

  • define getInstance () que devuelve la instancia única.
  • responsable de crear y administrar el objeto de instancia.

El objeto Singleton se implementa como una función anónima inmediata. La función se ejecuta inmediatamente envolviéndola entre paréntesis seguido de dos corchetes adicionales. Se llama anónimo porque no tiene nombre.

Programa de muestra,

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
fuente
1

Más simple / más limpio para mí significa también simplemente entender y sin campanas ni silbidos como se discute mucho en la versión Java de la discusión:

¿Cuál es una manera eficiente de implementar un patrón singleton en Java?

La respuesta que encajaría mejor desde el punto de vista más simple / más limpio es:

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

Y solo se puede traducir parcialmente a JavaScript. Algunas de las diferencias en Javascript son:

  • los constructores no pueden ser privados
  • Las clases no pueden tener campos declarados

Pero dada la última sintaxis de ECMA, es posible acercarse a:

Patrón Singleton como ejemplo de clase 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;
  }
}

Ejemplo de uso

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

Resultado de ejemplo

DefaultField1
DefaultField2
Wolfgang Fahl
fuente
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 estás en clases:

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

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

        this.name = name;
    }
}

Prueba:

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
fuente
1

Si quieres usar clases:

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 salida para lo anterior será:

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

Otra forma de escribir Singleton usando la función

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 salida para lo anterior será:

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