Respostas:
A diferença básica é que uma função construtora é usada com a new
palavra - chave (que faz com que o JavaScript crie automaticamente um novo objeto, defina this
dentro 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.
someMethod
os 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 someMethod
qual é algo que talvez não desejemos. É aí que o uso new
e prototype
a função de fábrica ajudariam.
new
com 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.
new
internamente 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 new
requisito), 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 new
um class
construtor, ou o construtor lançará um erro.
Se você fizer o instanceof
verificação, deixa ambiguidade quanto a ser ou não new
necessário. Na minha opinião, não deveria ser. Você efetivamente interrompeu o new
requisito, 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 this
se comporte mal, veja o próximo ponto).
this
se comporta como normalmente faria - para que você possa usá-lo para acessar o objeto pai (por exemplo, inside player.create()
, this
refere-se a player
, como qualquer outra invocação de método faria. call
e apply
també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.
new
não se comporta como o esperado (veja acima). Solução: não use.
this
nã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 () - this
refere-se a foo
- assim como qualquer outro método JavaScript - veja os benefícios).
new
viola o princípio de abrir / fechar. Consulte medium.com/javascript-scene/… para uma discussão muito maior do que esses comentários permitem.
new
palavra - chave, não acredito que a new
palavra - 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");
new
cria um objeto com prototipagem User.prototype
e chama User
com o objeto criado como seu this
valor.
new
trata uma expressão de argumento para seu operando como opcional:
let user = new User;
causaria new
a chamada User
sem argumentos.
new
retorna 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 prototype
e retornam true usando o instanceOf
operador na função de construtor.
Os comportamentos acima podem falhar se você alterar dinamicamente o valor da prototype
propriedade 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 class
palavra - chave.
As funções do construtor podem ser estendidas usando a extends
palavra - chave
As funções do construtor não podem retornar null
como 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 null
como 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 prototype
propriedade da função de fábrica e retornam false
de instanceOf factoryFunction
.
A função de fábrica não pode ser estendida com segurança usando a extends
palavra - chave, porque os objetos estendidos herdariam da prototype
propriedade de funções de fábrica, e não da prototype
propriedade 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 new
palavra-chave.