Respostas:
A diferença básica é que uma função construtora é usada com a newpalavra - chave (que faz com que o JavaScript crie automaticamente um novo objeto, defina thisdentro da função para esse objeto e retorne o objeto):
var objFromConstructor = new ConstructorFunction();
Uma função de fábrica é chamada como uma função "regular":
var objFromFactory = factoryFunction();
Mas, para ser considerada uma "fábrica", seria necessário retornar uma nova instância de algum objeto: você não a chamaria de função "fábrica" se apenas retornasse um valor booleano ou algo assim. Isso não acontece automaticamente como com new, mas permite mais flexibilidade para alguns casos.
Em um exemplo muito simples, as funções mencionadas acima podem ser algo como isto:
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;
}
É claro que você pode tornar as funções de fábrica muito mais complicadas do que esse exemplo simples.
Uma vantagem das funções de fábrica é quando o objeto a ser retornado pode ter vários tipos diferentes, dependendo de algum parâmetro.
someMethodos objetos devolvidos pela fábrica e é aí que fica um pouco nebuloso. Dentro da função de fábrica, se alguém o fizer var obj = { ... , someMethod: function() {}, ... }, isso levaria a cada objeto retornado, mantenha uma cópia diferente da someMethodqual é algo que talvez não desejemos. É aí que o uso newe prototypea função de fábrica ajudariam.
newcom a função construtora; Eu pensei que era onde seria necessário ver um exemplo de como substituir construtores por funções de fábrica e é aí que eu achava que a consistência nos exemplos era necessária. De qualquer forma, a resposta é informativa o suficiente. Esse era apenas um ponto que eu queria levantar, não que eu estivesse diminuindo a qualidade da resposta de alguma forma.
newinternamente ou Object.create()criar um objeto com um protótipo específico.
A maioria dos livros ensina você a usar construtores e new
this refere-se ao novo objeto
Algumas pessoas gostam da maneira como var myFoo = new Foo();lê.
Os detalhes da instanciação vazam para a API de chamada (por meio do newrequisito), para que todos os chamadores estejam fortemente acoplados à implementação do construtor. Se você precisar da flexibilidade adicional da fábrica, precisará refatorar todos os chamadores (reconhecidamente o caso excepcional, e não a regra).
Esquecer newé um bug tão comum, você deve considerar fortemente adicionar uma verificação padrão para garantir que o construtor seja chamado corretamente ( if (!(this instanceof Foo)) { return new Foo() }). EDIT: Desde ES6 (ES2015), você não pode esquecer newum classconstrutor, ou o construtor lançará um erro.
Se você fizer o instanceof verificação, deixa ambiguidade quanto a ser ou não newnecessário. Na minha opinião, não deveria ser. Você efetivamente interrompeu o newrequisito, o que significa que você pode apagar a desvantagem nº 1. Mas você terá uma função de fábrica com apenas o nome , com clichê adicional, uma letra maiúscula e menos flexívelthis contexto .
Mas minha principal preocupação é que viole o princípio de abrir / fechar. Você começa a exportar um construtor, os usuários começam a usá-lo e, depois, no caminho, percebe que precisa da flexibilidade de uma fábrica (por exemplo, para alternar a implementação para usar conjuntos de objetos ou para instanciar contextos de execução ou para ter mais flexibilidade de herança usando OO prototípico).
Você está preso, no entanto. Você não pode fazer a alteração sem quebrar todo o código que chama seu construtor comnew . Você não pode mudar para usar pools de objetos para obter ganhos de desempenho, por exemplo.
Além disso, o uso de construtores fornece uma enganosa instanceof que não funciona em contextos de execução e não funciona se o seu protótipo de construtor for trocado. Também falhará se você começar retornandothis do construtor e depois mudar para exportar um objeto arbitrário, o que você teria que fazer para habilitar o comportamento de fábrica no construtor.
Menos código - não requer clichê.
Você pode retornar qualquer objeto arbitrário e usar qualquer protótipo arbitrário - oferecendo mais flexibilidade para criar vários tipos de objetos que implementam a mesma API. Por exemplo, um media player que pode criar instâncias de HTML5 e flash players, ou uma biblioteca de eventos que pode emitir eventos DOM ou soquetes da web. As fábricas também podem instanciar objetos em contextos de execução, tirar proveito dos pools de objetos e permitir modelos de herança prototípica mais flexíveis.
Você nunca precisaria converter de uma fábrica para um construtor; portanto, a refatoração nunca será um problema.
Nenhuma ambiguidade sobre o uso new. Não. (Isso fará com que thisse comporte mal, veja o próximo ponto).
thisse comporta como normalmente faria - para que você possa usá-lo para acessar o objeto pai (por exemplo, inside player.create(), thisrefere-se a player, como qualquer outra invocação de método faria. calle applytambém reatribuirthis , conforme o esperado. Se você armazenar protótipos no objeto pai, isso pode ser uma ótima maneira de trocar dinamicamente a funcionalidade e permitir um polimorfismo muito flexível para a instanciação do objeto.
Nenhuma ambiguidade em capitalizar ou não. Não. As ferramentas de cotão reclamarão, e você será tentado a usá new-lo e depois desfará o benefício descrito acima.
Algumas pessoas gostam do jeito var myFoo = foo();ou var myFoo = foo.create();leem.
newnão se comporta como o esperado (veja acima). Solução: não use.
thisnão se refere ao novo objeto (em vez disso, se o construtor for chamado com notação de ponto ou colchete, por exemplo, foo.bar () - thisrefere-se a foo- assim como qualquer outro método JavaScript - veja os benefícios).
newviola o princípio de abrir / fechar. Consulte medium.com/javascript-scene/… para uma discussão muito maior do que esses comentários permitem.
newpalavra - chave, não acredito que a newpalavra - chave realmente forneça legibilidade adicional. Na IMO, parece bobagem pular os bastidores para permitir que os chamadores digitem mais.
Um construtor retorna uma instância da classe em que você a chama. Uma função de fábrica pode retornar qualquer coisa. Você usaria uma função de fábrica quando precisar retornar valores arbitrários ou quando uma classe tiver um grande processo de configuração.
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Jack");
newcria um objeto com prototipagem User.prototypee chama Usercom o objeto criado como seu thisvalor.
new trata uma expressão de argumento para seu operando como opcional:
let user = new User;
causaria newa chamada Usersem argumentos.
newretorna o objeto que ele criou, a menos que o construtor retorne um valor de objeto , que é retornado. Este é um caso extremo que, na maior parte, pode ser ignorado.
Objetos criados por funções de construtor herdam propriedades da propriedade do construtor prototypee retornam true usando o instanceOfoperador na função de construtor.
Os comportamentos acima podem falhar se você alterar dinamicamente o valor da prototypepropriedade do construtor depois de já ter usado o construtor. Fazer isso é raro e não pode ser alterado se o construtor tiver sido criado usando a classpalavra - chave.
As funções do construtor podem ser estendidas usando a extendspalavra - chave
As funções do construtor não podem retornar nullcomo um valor de erro. Como não é um tipo de dados do objeto, é ignorado por new.
function User(name, age) {
return {
name,
age,
}
};
let user = User("Tom", 23);
Aqui a função de fábrica é chamada sem new . A função é totalmente responsável pelo uso direto ou indireto se seus argumentos e o tipo de objeto que ela retornar. Neste exemplo, ele retorna um simples [Objeto de objeto] com algumas propriedades definidas a partir de argumentos.
Oculta facilmente as complexidades de implementação da criação de objetos do chamador. Isso é particularmente útil para funções de código nativo em um navegador.
A função de fábrica nem sempre retorna objetos do mesmo tipo e pode até retornar nullcomo um indicador de erro.
Em casos simples, as funções de fábrica podem ser simples em estrutura e significado.
Os objetos retornados geralmente não herdam da prototypepropriedade da função de fábrica e retornam falsede instanceOf factoryFunction.
A função de fábrica não pode ser estendida com segurança usando a extendspalavra - chave, porque os objetos estendidos herdariam da prototypepropriedade de funções de fábrica, e não da prototypepropriedade do construtor usado pela função de fábrica.
As fábricas são "sempre" melhores. Ao usar linguagens orientadas a objetos,
As implementações (os objetos reais criados com o novo) não são expostas ao usuário / consumidor da fábrica. Isso significa que o desenvolvedor da fábrica pode expandir e criar novas implementações, desde que não rompa o contrato ... e permite que o consumidor da fábrica se beneficie apenas da nova API sem precisar alterar o código ... se eles usaram new e uma implementação "nova" aparece, eles precisam mudar todas as linhas que usam "new" para usar a "nova" implementação ... com a fábrica, seu código não muda ...
Fábricas - melhor do que qualquer outra coisa - a estrutura da primavera é completamente construída em torno dessa idéia.
As fábricas são uma camada de abstração e, como todas as abstrações, têm um custo de complexidade. Ao encontrar uma API baseada na fábrica, descobrir qual é a fábrica para uma determinada API pode ser um desafio para o consumidor da API. Com os construtores, a descoberta é trivial.
Ao decidir entre os médicos e as fábricas, você precisa decidir se a complexidade é justificada pelo benefício.
Vale ressaltar que os construtores Javascript podem ser fábricas arbitrárias retornando algo diferente disso ou indefinido. Assim, em js, você pode obter o melhor dos dois mundos - API detectável e pool / cache de objetos.
new, Alterando o comportamento de this, Alterando o valor de retorno, Conectando um ref de protótipo, Ativando instanceof(que está e não deve ser usado para essa finalidade). Aparentemente, todos esses são "recursos". Na prática, eles prejudicam a qualidade do seu código.
Para as diferenças, Eric Elliott esclareceu muito bem,
Mas para a segunda pergunta:
Quando usar um em vez do outro?
Se você é proveniente do plano de fundo orientado a objetos, a função Constructor parece mais natural para você. Dessa forma, você não deve esquecer de usar a newpalavra-chave.