react - javascript singleton pregunta
react singleton (10)
Acabo de leer algunos hilos en la discusión del diseño de singleton en javascript. Soy 100% nuevo en el diseño de patrones, pero como veo, ya que un Singleton por definición no tendrá la necesidad de crear una instancia, conceptualmente, si no se debe crear una instancia, en mi opinión no tiene que ser tratado como Objetos convencionales que se crean a partir de un plano (clases). Entonces, lo que me sorprende es por qué no solo pensar en un singleton simplemente como algo estáticamente disponible que está envuelto en algún tipo de alcance y que debería ser todo.
De los hilos que vi, la mayoría de ellos hacen un singleton a través de javascript tradicional
new function(){}
seguido de hacer un pseudo constructor.
Bueno, solo creo que un objeto literal es suficiente:
var singleton = {
dothis: function(){},
dothat: function(){}
}
¿Correcto? ¿O alguien tiene mejores ideas?
[actualización]: una vez más, lo que quiero decir es por qué la gente no usa una forma más sencilla de hacer singletons en javascript como lo mostraba en el segundo fragmento, si hay una razón absoluta, por favor dígamelo. Por lo general tengo miedo de este tipo de situaciones que simplifico mucho las cosas: D
Crockford (parece) de acuerdo en que el objeto literal es todo lo que necesita para un singleton en JavaScript:
El último cuadro de código muestra lo que he visto a los desarrolladores de JS llamar a su versión de diseño OO en Javascript.
Los singetones están destinados a ser objetos singulares que no pueden construirse (excepto, supongo, en la definición inicial. Usted tiene uno, instancia global de un singleton.
El patrón de singleton se implementa creando una clase con un método que crea una nueva instancia de la clase si no existe una. Si ya existe una instancia, simplemente devuelve una referencia a ese objeto. 1
(function (global) {
var singleton;
function Singleton () {
// singleton does have a constructor that should only be used once
this.foo = "bar";
delete Singleton; // disappear the constructor if you want
}
global.singleton = function () {
return singleton || (singleton = new Singleton());
};
})(window);
var s = singleton();
console.log(s.foo);
var y = singleton();
y.foo = "foo";
console.log(s.foo);
No solo declara el singleton como un objeto porque eso lo instancia, no lo declara. Tampoco proporciona un mecanismo para el código que no conoce una referencia previa al singleton para recuperarlo. El singleton no es el objeto / clase que devuelve el singleton, es una estructura. Esto es similar a cómo las variables cerradas no son cierres, el alcance de la función que proporciona el cierre es el cierre.
El punto de usar el "pseudo constructor" es que crea un nuevo ámbito variable. Puede declarar variables locales dentro de la función que están disponibles dentro de cualquier función anidada pero no desde el ámbito global.
En realidad hay dos maneras de hacerlo. Puede llamar a la función con new
como en su ejemplo, o simplemente llamar a la función directamente. Hay pequeñas diferencias en cómo escribiría el código, pero son esencialmente equivalentes.
Tu segundo ejemplo podría escribirse así:
var singleton = new function () {
var privateVariable = 42; // This can be accessed by dothis and dothat
this.dothis = function () {
return privateVariable;
};
this.dothat = function () {};
}; // Parentheses are allowed, but not necessary unless you are passing parameters
o
var singleton = (function () {
var privateVariable = 42; // This can be accessed by dothis and dothat
return {
dothis: function () {
return privateVariable;
},
dothat: function () {}
};
})(); // Parentheses are required here since we are calling the function
También puede pasar argumentos a cualquiera de las funciones (deberá agregar paréntesis al primer ejemplo).
Estoy de acuerdo contigo, la forma más sencilla es usar un objeto literal, pero si quieres miembros privados , puedes implementar aprovechando los closures :
var myInstance = (function() {
var privateVar;
function privateMethod () {
// ...
}
return { // public interface
publicMethod1: function () {
// private members can be accessed here
},
publicMethod2: function () {
// ...
}
};
})();
Sobre la new function(){}
construcción de la new function(){}
, simplemente usará una función anónima como una constructor function
, el contexto dentro de esa función será un nuevo objeto que será devuelto.
Edición: En respuesta al comentario de @ J5 , que es simple de hacer, en realidad creo que este puede ser un buen ejemplo para usar un patrón de definición de función perezosa :
function singleton() {
var instance = (function() {
var privateVar;
function privateMethod () {
// ...
}
return { // public interface
publicMethod1: function () {
// private members can be accessed here
},
publicMethod2: function () {
// ...
}
};
})();
singleton = function () { // re-define the function for subsequent calls
return instance;
};
return singleton(); // call the new function
}
Cuando se llama a la función la primera vez, hago la instancia de objeto y reasigno singleton
a una nueva función que tiene esa instancia de objeto en su cierre.
Antes del final de la primera llamada, ejecuto la función singleton
redefinida que devolverá la instancia creada.
Las siguientes llamadas a la función singleton
simplemente devolverán la instance
que está almacenada en su cierre, porque la nueva función es la que se ejecutará.
Puedes probar que comparando el objeto devuelto:
singleton() == singleton(); // true
El operador ==
para objetos devolverá true
solo si la referencia de objeto de ambos operandos es la misma, devolverá falso incluso si los objetos son idénticos pero son dos instancias diferentes:
({}) == ({}); // false
new Object() == new Object(); // false
He usado la segunda versión ( var singleton = {};
) para todo, desde las extensiones de Firefox hasta los sitios web, y funciona muy bien. Una buena idea es no definir las cosas dentro de los corchetes, pero fuera de ella con el nombre del objeto, así:
var singleton = {};
singleton.dothis = function(){
};
singleton.someVariable = 5;
La especificación ES5 nos permite usar Object.create ():
var SingletonClass = (function() {
var instance;
function SingletonClass() {
if (instance == null) {
instance = Object.create(SingletonClass.prototype);
}
return instance;
}
return {
getInstance: function() {
return new SingletonClass();
}
};
})();
var x = SingletonClass.getInstance();
var y = SingletonClass.getInstance();
var z = new x.constructor();
Esto es bueno, ya que no tenemos que preocuparnos por las fugas de nuestro constructor, siempre terminamos con la misma instancia.
Esta estructura también tiene la ventaja de que nuestro Singleton no se construye solo hasta que se requiere. Además, usar el cierre como lo hacemos aquí evita que el código externo use nuestra variable de "instancia", accidental o de otra manera. Podemos crear más variables privadas en el mismo lugar y podemos definir cualquier cosa que nos importe exportar públicamente en nuestro prototipo de clase.
Qué tal esto:
function Singleton() {
// ---------------
// Singleton part.
// ---------------
var _className = null;
var _globalScope = null;
if ( !(this instanceof arguments.callee) ) {
throw new Error("Constructor called as a function.");
}
if ( !(_className = arguments.callee.name) ) {
throw new Error("Unable to determine class name.")
}
_globalScope = (function(){return this;}).call(null);
if ( !_globalScope.singletons ) {
_globalScope.singletons = [];
}
if ( _globalScope.singletons[_className] ) {
return _globalScope.singletons[_className];
} else {
_globalScope.singletons[_className] = this;
}
// ------------
// Normal part.
// ------------
var _x = null;
this.setx = function(val) {
_x = val;
}; // setx()
this.getx = function() {
return _x;
}; // getx()
function _init() {
_x = 0; // Whatever initialisation here.
} // _init()
_init();
} // Singleton()
var p = new Singleton;
var q = new Singleton;
p.setx(15);
q.getx(); // returns 15
Robé esto de la respuesta de / CMS , y lo cambié para que pueda ser invocado como:
MySingleton.getInstance().publicMethod1();
Con la ligera alternancia:
var MySingleton = { // These two lines
getInstance: function() { // These two lines
var instance = (function() {
var privateVar;
function privateMethod () {
// ...
console.log( "b" );
}
return { // public interface
publicMethod1: function () {
// private members can be accessed here
console.log( "a" );
},
publicMethod2: function () {
// ...
privateMethod();
}
};
})();
singleton = function () { // re-define the function for subsequent calls
return instance;
};
return singleton(); // call the new function
}
}
También me he preguntado acerca de esto, pero solo para mí es razonable definir un objeto con funciones. No tiene sentido crear un constructor al que nadie debe llamar, para crear un objeto sin prototipo, cuando solo puede definir el objeto directamente.
Por otro lado, si desea que su singleton sea una instancia de alguna "clase" existente, es decir, desea que tenga algún otro objeto como prototipo, entonces necesita usar una función constructora, de modo que puede establecer su propiedad prototype
antes de llamarla.