Respuestas:
La diferencia básica es que se usa una función de constructor con la newpalabra clave (lo que hace que JavaScript cree automáticamente un nuevo objeto, se establezca thisdentro de la función en ese objeto y devuelva el objeto):
var objFromConstructor = new ConstructorFunction();
Una función de fábrica se llama como una función "regular":
var objFromFactory = factoryFunction();
Pero para que se considere una "fábrica" necesitaría devolver una nueva instancia de algún objeto: no lo llamaría una función de "fábrica" si solo devuelve un valor booleano o algo así. Esto no sucede automáticamente como con new, pero permite más flexibilidad en algunos casos.
En un ejemplo realmente simple, las funciones a las que se hace referencia anteriormente podrían verse así:
function ConstructorFunction() {
this.someProp1 = "1";
this.someProp2 = "2";
}
ConstructorFunction.prototype.someMethod = function() { /* whatever */ };
function factoryFunction() {
var obj = {
someProp1 : "1",
someProp2 : "2",
someMethod: function() { /* whatever */ }
};
// other code to manipulate obj in some way here
return obj;
}
Por supuesto, puede hacer que las funciones de fábrica sean mucho más complicadas que ese simple ejemplo.
Una ventaja de las funciones de fábrica es cuando el objeto a devolver podría ser de varios tipos diferentes, dependiendo de algún parámetro.
someMethodlos objetos devueltos por la fábrica, y ahí es donde se pone un poco nublado. Dentro de la función de fábrica, si uno solo lo hace var obj = { ... , someMethod: function() {}, ... }, eso llevaría a que cada objeto devuelto mantenga una copia diferente de la someMethodcual es algo que podríamos no querer. Ahí es donde usar newy prototypedentro de la función de fábrica ayudaría.
newcon la función de constructor; Pensé que es donde uno podría necesitar ver un ejemplo de cómo reemplazar constructores con funciones de fábrica y ahí es donde pensé que se requería la coherencia en los ejemplos. De todos modos, la respuesta es suficientemente informativa. Este era solo un punto que quería plantear, no es que esté reduciendo la calidad de la respuesta de ninguna manera.
newinternamente o Object.create()para crear un objeto con un prototipo específico.
La mayoría de los libros te enseñan a usar constructores y new
this se refiere al nuevo objeto
A algunas personas les gusta la forma de var myFoo = new Foo();leer.
Los detalles de la instanciación se filtran en la API de llamada (a través del newrequisito), por lo que todas las personas que llaman están estrechamente vinculadas a la implementación del constructor. Si alguna vez necesita la flexibilidad adicional de la fábrica, tendrá que refactorizar a todas las personas que llaman (ciertamente, el caso excepcional, en lugar de la regla).
Olvidar newes un error tan común, debe considerar agregar una verificación repetitiva para asegurarse de que el constructor se llame correctamente ( if (!(this instanceof Foo)) { return new Foo() }). EDITAR: desde ES6 (ES2015) no se puede olvidar newcon un classconstructor, o el constructor arrojará un error.
Si realiza la instanceofverificación, deja ambigüedad en cuanto a si newse requiere o no . En mi opinión, no debería ser así. Efectivamente ha cortocircuitado el newrequisito, lo que significa que podría borrar el inconveniente # 1. Pero entonces tienes una función de fábrica en todo menos en nombre , con repetitivo adicional, una letra mayúscula y un thiscontexto menos flexible .
Pero mi principal preocupación es que viola el principio abierto / cerrado. Comienza exportando un constructor, los usuarios comienzan a usar el constructor, luego, en el futuro, se da cuenta de que necesita la flexibilidad de una fábrica, en su lugar (por ejemplo, para cambiar la implementación para usar grupos de objetos, o para crear instancias en contextos de ejecución, o para tener más flexibilidad de herencia usando prototipado OO).
Sin embargo, estás atrapado. No puede realizar el cambio sin romper todo el código que llama a su constructor new. No puede cambiar a usar agrupaciones de objetos para aumentar el rendimiento, por ejemplo.
Además, el uso de constructores le da un engaño instanceofque no funciona en contextos de ejecución, y no funciona si su prototipo de constructor se intercambia. También fallará si comienza a regresar thisde su constructor, y luego cambia a exportar un objeto arbitrario, lo que tendría que hacer para habilitar un comportamiento de fábrica en su constructor.
Menos código: no se requiere repetitivo.
Puede devolver cualquier objeto arbitrario y usar cualquier prototipo arbitrario, lo que le brinda más flexibilidad para crear varios tipos de objetos que implementan la misma API. Por ejemplo, un reproductor multimedia que puede crear instancias de HTML5 y reproductores flash, o una biblioteca de eventos que puede emitir eventos DOM o eventos de socket web. Las fábricas también pueden crear instancias de objetos en contextos de ejecución, aprovechar las agrupaciones de objetos y permitir modelos de herencia de prototipos más flexibles.
Nunca necesitaría convertir de una fábrica a un constructor, por lo que la refactorización nunca será un problema.
No hay ambigüedad sobre el uso new. No lo hagas (Hará que se thiscomporte mal, vea el siguiente punto).
thisse comporta como lo haría normalmente, por lo que puede usarlo para acceder al objeto primario (por ejemplo, en el interior player.create(), se thisrefiere player, como cualquier otra invocación de método, cally applytambién reasignar this, como se esperaba. Si almacena prototipos en el objeto primario, eso puede ser una excelente manera de intercambiar dinámicamente la funcionalidad y permitir un polimorfismo muy flexible para la creación de instancias de su objeto.
No hay ambigüedad sobre si capitalizar o no. No lo hagas Las herramientas de pelusa se quejarán, y luego sentirás la tentación de intentar usarlas new, y luego deshacerás el beneficio descrito anteriormente.
A algunas personas les gusta el camino var myFoo = foo();o las var myFoo = foo.create();lecturas.
newno se comporta como se esperaba (ver arriba). Solución: no lo use.
thisno se refiere al nuevo objeto (en cambio, si el constructor se invoca con notación de punto o notación de corchetes, por ejemplo, foo.bar () - se thisrefiere a foo, al igual que cualquier otro método de JavaScript, vea los beneficios).
newviola el principio abierto / cerrado. Visite medium.com/javascript-scene/… para una discusión mucho más amplia que la que permiten estos comentarios.
newpalabra clave, no creo que la newpalabra clave realmente proporcione ninguna legibilidad adicional. En mi opinión, parece una tontería saltar a través de los aros para permitir a las personas que llaman escribir más.
Un constructor devuelve una instancia de la clase a la que la llama. Una función de fábrica puede devolver cualquier cosa. Usaría una función de fábrica cuando necesite devolver valores arbitrarios o cuando una clase tenga un proceso de configuración grande.
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Jack");
newcrea un objeto prototipo User.prototypey llama Usercon el objeto creado como su thisvalor.
new trata una expresión de argumento para su operando como opcional:
let user = new User;
causaría newllamar Usersin argumentos.
newdevuelve el objeto que creó, a menos que el constructor devuelva un valor de objeto , que se devuelve en su lugar. Este es un caso extremo que en su mayor parte puede ignorarse.
Los objetos creados por las funciones de constructor heredan propiedades de la propiedad del constructor prototypey devuelven verdadero usando el instanceOfoperador en la función de constructor.
Los comportamientos anteriores pueden fallar si cambia dinámicamente el valor de la prototypepropiedad del constructor después de haber usado el constructor. Hacerlo es raro , y no se puede cambiar si el constructor se creó con la classpalabra clave.
Las funciones de constructor se pueden ampliar utilizando la extendspalabra clave.
Las funciones del constructor no pueden regresar nullcomo un valor de error. Como no es un tipo de datos de objeto, es ignorado por new.
function User(name, age) {
return {
name,
age,
}
};
let user = User("Tom", 23);
Aquí la función de fábrica se llama sin new. La función es totalmente responsable del uso directo o indirecto si sus argumentos y el tipo de objeto que devuelve. En este ejemplo, devuelve un [Objeto objeto] simple con algunas propiedades establecidas a partir de argumentos.
Oculta fácilmente las complejidades de implementación de la creación de objetos de la persona que llama. Esto es particularmente útil para las funciones de código nativo en un navegador.
La función de fábrica no siempre necesita devolver objetos del mismo tipo, e incluso podría regresar nullcomo un indicador de error.
En casos simples, las funciones de fábrica pueden ser simples en estructura y significado.
Los objetos devueltos generalmente no heredan de la prototypepropiedad de la función de fábrica y regresan falsede instanceOf factoryFunction.
La función de fábrica no se puede extender de forma segura utilizando la extendspalabra clave porque los objetos extendidos heredarían de la prototypepropiedad de funciones de fábrica en lugar de la prototypepropiedad del constructor utilizado por la función de fábrica.
Las fábricas son "siempre" mejores. Cuando se usan lenguajes orientados a objetos, entonces
Las implementaciones (los objetos reales creados con nuevos) no están expuestos al usuario / consumidor de fábrica. Esto significa que el desarrollador de la fábrica puede expandirse y crear nuevas implementaciones siempre que no rompa el contrato ... y permite que el consumidor de la fábrica se beneficie de la nueva API sin tener que cambiar su código ... si usaron una nueva y aparece una "nueva" implementación, entonces tienen que ir y cambiar cada línea que usa "nueva" para usar la "nueva" implementación ... con la fábrica, su código no cambia ...
Fábricas, mejor que cualquier otra cosa, el marco de resorte está completamente construido alrededor de esta idea.
Las fábricas son una capa de abstracción y, como todas las abstracciones, tienen un costo complejo. Al encontrar una API basada en la fábrica, descubrir cuál es la fábrica para una API determinada puede ser un desafío para el consumidor de API. Con los constructores, la capacidad de descubrimiento es trivial.
Al decidir entre fábricas y fábricas, debe decidir si la complejidad está justificada por el beneficio.
Vale la pena señalar que los constructores de Javascript pueden ser fábricas arbitrarias al devolver algo diferente a esto o indefinido. Por lo tanto, en js puede obtener lo mejor de ambos mundos: API reconocible y agrupación / almacenamiento en caché de objetos.
new, Alterar el comportamiento de this, Alterar el valor de retorno, Conectar un prototipo de referencia, Habilitar instanceof(que yace y no debe usarse para este propósito). Aparentemente, todas esas son "características". En la práctica, dañan la calidad de su código.