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


14 Answers

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

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

Después, puede invocar la función como

var test = SingletonClass.getInstance();
Question

¿Cuál es la forma más simple / limpia de implementar un patrón singleton en JavaScript?




Hay más de una manera de despellejar a un gato :) Dependiendo de su gusto o necesidad específica, puede aplicar cualquiera de las soluciones propuestas. Personalmente busco la primera solución de CMS siempre que sea posible (cuando no se necesita privacidad). Dado que la pregunta era sobre la más simple y más limpia, ese es el ganador. 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 ningún vars privado, así que es más o menos lo mismo que:

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



I believe this is the simplest/cleanest and most intuitive way though it requires ES7:

export default class Singleton {

  static instance;

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

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

}

The source code is from: adam-bien.com




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



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

Nosotros probamos:

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



Me gusta utilizar una combinación de Singleton con el patrón de módulo, bifurcación de tiempo de inicio con un control Global NS, envuelto dentro de un cierre. En un caso donde el entorno no va a cambiar después de la inicialización del singleton; el uso de un literal de objeto invocado inmediatamente para devolver un módulo lleno de utilidades que persistirán durante algún tiempo debería estar bien. I'm not passing any dependencies, just invoking the singletons within their own little world - the only goal being to: create a utilities module for event binding / unbinding (device orientation / orientation changes could also work in this case).

window.onload = ( function( _w ) {
            console.log.apply( console, ['it', 'is', 'on'] );
            ( {
                globalNS : function() {
                    var nameSpaces = ["utils", "eventUtils"],
                        nsLength = nameSpaces.length,
                        possibleNS = null;

                    outerLoop:
                    for ( var i = 0; i < nsLength; i++ ) {
                        if ( !window[nameSpaces[i]] ) {
                            window[nameSpaces[i]] = this.utils;
                            break outerLoop;
                        };
                    };
                },
                utils : {
                    addListener : null,
                    removeListener : null
                },
                listenerTypes : {
                    addEvent : function( el, type, fn ) {
                        el.addEventListener( type, fn, false );
                    },
                    removeEvent : function( el, type, fn ) {
                        el.removeEventListener( type, fn, false );
                    },
                    attachEvent : function( el, type, fn ) {
                        el.attachEvent( 'on'+type, fn );
                    },
                    detatchEvent : function( el, type, fn ) {
                        el.detachEvent( 'on'+type, fn );
                    }
                },
                buildUtils : function() {
                    if ( typeof window.addEventListener === 'function' ) {
                        this.utils.addListener = this.listenerTypes.addEvent;
                        this.utils.removeListener = this.listenerTypes.removeEvent;
                    } else {
                        this.utils.attachEvent = this.listenerTypes.attachEvent;
                        this.utils.removeListener = this.listenerTypes.detatchEvent;
                    };
                    this.globalNS();
                },
                init : function() {
                    this.buildUtils();
                }
            } ).init();
        }( window ) );



Necesitaba varios singletons con:

  • inicialización perezosa
  • parámetros iniciales

y entonces 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;
}
  • args debe ser Array para que esto funcione, por lo tanto, si tiene variables vacías, simplemente pase []

  • Utilicé el objeto 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 funcione la ventana [], pero con algunas simples verificaciones de tipo, window.name y window.construct también son posibles.




No estoy seguro de por qué nadie planteó esto, pero podría hacer:

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

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



¿Qué tal de esta manera, solo asegurar que la clase no puede nuevo de nuevo.

Con esto, puedes usar la instanceof op, también, puedes usar la cadena de prototipos para heredar la clase, es una clase regular, pero no puedes renovarla, si quieres obtener la instancia simplemente usa getInstance

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 el miembro de la instance , simplemente colóquelo en un cierre.




puedo poner mis 5 monedas Tengo una función de constructor, ej.

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

Lo que tengo que hacer es que cada objeto creado por este CF sea el mismo.

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

prueba

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



Semifallo:

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

El patrón Singleton limita el número de instancias de un objeto en particular a solo uno. Esta única instancia 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 corchetes seguida de dos corchetes adicionales. Se llama anónimo porque no tiene un nombre.

Programa de ejemplo,

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




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

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

  • una y solo una instancia del objeto de función puede ser instanciada
  • la instancia no está públicamente disponible 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 instanciar si aún no hay una instancia disponible
  • la declaración del constructor debe permitir que se modifique su cadena de prototipos. Esto permitirá al constructor heredar de otros prototipos y ofrecer métodos "públicos" para la instancia

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

// 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 autoejecutable es en sí misma un Singleton, como se demostró muy bien en el código provisto por @CMS. La única pega aquí es que no es posible modificar la cadena prototipo del constructor cuando el propio constructor 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 aprovechando las reglas de JavaScript de la disponibilidad del alcance funcional.




Main key is to Undertand Closure importance behind this.So property even inside the inner function will be private with the help of closure.

var Singleton = function () { var instance;

 function init() {

    function privateMethod() {
        console.log("private via closure");
    }

    var privateVariable = "Private Property";

    var privateRandomNumber = Math.random();// this also private

    return {
        getRandomNumber: function () {  // access via getter in init call
            return privateRandomNumber;
        }

    };

};

return {
    getInstance: function () {

        if (!instance) {
            instance = init();
        }

        return instance;
    }

};

};




Obtuve este ejemplo de los patrones de JavaScript. Cree mejores aplicaciones con patrones de codificación y diseño. Por el libro de Stoyan Stefanov en caso de que necesite alguna clase de implementación simple como el 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 verificar 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 menos recomendable debido a que la instancia está expuesta al público.

jsFiddly demo.




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 el tiempo de ejecución de JavaScript tenga decoradores realmente habilitados o que use compiladores como Babel y TypeScript.

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






Related