Esta definición encaja muy bien en los lenguajes con una orientación a objetos clásica, como Java, pero JavaScript no tiene clases, su orientación a objetos se basa en prototipos. Esta particularidad, junto con el uso de funciones constructoras para crear algo parecido a una clase, hace que este patrón pueda implementarse de muchas maneras diferentes.
El singleton como un objeto literal
Puesto que el objetivo es que solamente exista un objeto concreto de un tipo ¿ porqué no crearlo directamente ?. En JavaScript podemos crear objetos sin necesidad de tener una clase ni un constructor. Esta es la forma más sencilla de crear un singleton:var mySingleton = { property1: 1, property2: 2, method1: function() {} };
Si necesitamos tener variables y métodos privados, podemos utilizar el patrón módulo para crear el objeto:
var mySingleton = (function() { var privateProperty1 = "one"; var privateMethod1 = function () {}; return { publicProperty1: 1, publicProperty2: 2, publicMethod1: function() { //use private props and methods here } }; })();
Un singleton más clásico
También podemos implementar el patrón de una forma más parecida al patrón clásico, de forma que tengamos una 'clase' ( realmente una función constructora ) que no se pueda instanciar y con un método público 'getInstance' que nos devuelva el objeto si ya existe o lo cree si no está creado:var mySingleton = (function() { //Singleton constructor is private function Singleton() { var privateVar1 = "I'm a private var"; this.publicVar1 = "I'm a public var"; this.publicMethod1 = function() { console.log("Private var: " + privateVar1 + " public var: " + this.publicVar1); } } //private var to store the single instance var singleInstance; //Return the object providing getInstance method return { getInstance: function() { if (!singleInstance) singleInstance = new Singleton(); return singleInstance; } } })()
En este caso, lo que tenemos en 'mySingleton' es un objeto que proporciona el método público 'getInstance'. Para conseguir la instancia hacemos:
var myInstance = mySingleton.getInstance();
Utilizando el operador 'new'
Podemos hacerlo aún más sencillo de usar, de forma que que el usuario no tenga que saber que está instanciando un singleton ( llamando a getInstance ). Puede hacer 'new' como con cualquier otra clase y obtener la instancia única.Esta sería nuestra clase ( nuestra función constructora ):
function MySingleton () { if ( MySingleton.singleInstance ) return MySingleton.singleInstance; MySingleton.singleInstance = this; this.publicProperty1 = 1; this.publicProperty2 = 2; this.publicMethod1: function() {}; }
Podemos instanciar la clase con 'new' todas las veces que queramos, obtendremos siempre la misma instancia:
var mySingletonInstance1 = new MySingleton(); var mySingletonInstance2 = new MySingleton(); var mySingletonInstance3 = new MySingleton(); // mySingletonInstance1 === mySingletonInstance2 === mySingletonInstance3
Podemos mejorar aún más el código evitando usar una propiedad pública (MySingleton.singleInstance) para guardar nuestra instancia. Es mejor utilizar una variable privada de la siguiente forma:
var MySingleton = (function() { var singleInstance; return function() { if ( singleInstance ) return singleInstance; singleInstance = this; this.publicProperty1 = 1; this.publicProperty2 = 2; this.publicMethod1: function() {}; } })();
El uso sería igual que en el ejemplo anterior:
var mySingletonInstance1 = new MySingleton(); var mySingletonInstance2 = new MySingleton(); var mySingletonInstance3 = new MySingleton(); // mySingletonInstance1 === mySingletonInstance2 === mySingletonInstance3