Risposte:
La differenza di base è che una funzione di costruzione viene utilizzata con la new
parola chiave (che fa sì che JavaScript crei automaticamente un nuovo oggetto, imposti this
la funzione su quell'oggetto e restituisca l'oggetto):
var objFromConstructor = new ConstructorFunction();
Una funzione di fabbrica è chiamata come una funzione "normale":
var objFromFactory = factoryFunction();
Ma per essere considerato una "fabbrica" dovrebbe restituire una nuova istanza di qualche oggetto: non la chiamereste una funzione "fabbrica" se restituisse un valore booleano o qualcosa del genere. Questo non accade automaticamente come connew
, ma in alcuni casi consente una maggiore flessibilità.
In un esempio davvero semplice, le funzioni sopra menzionate potrebbero apparire in questo modo:
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;
}
Ovviamente puoi rendere le funzioni di fabbrica molto più complicate di quel semplice esempio.
Un vantaggio per le funzioni di fabbrica è quando l'oggetto da restituire potrebbe essere di diversi tipi a seconda di alcuni parametri.
someMethod
gli oggetti restituiti dalla factory, ed è lì che diventa un po 'confuso. All'interno della funzione di fabbrica, se solo lo fa var obj = { ... , someMethod: function() {}, ... }
, ciò porterebbe a ogni oggetto restituito contenere una copia diversa di someMethod
ciò che potremmo non desiderare. Ecco dove sarebbe utile usare new
e prototype
all'interno della funzione di fabbrica.
new
con la funzione di costruzione; Ho pensato che fosse dove si potrebbe aver bisogno di vedere come sostituire i costruttori con l' esempio delle funzioni di fabbrica ed è qui che ho pensato che fosse necessaria la coerenza negli esempi. Comunque, la risposta è abbastanza istruttiva. Questo era solo un punto che volevo sollevare, non che sto abbassando la qualità della risposta in alcun modo.
new
internamente o utilizzare Object.create()
per creare un oggetto con un prototipo specifico.
La maggior parte dei libri ti insegnano a usare costruttori e new
this
si riferisce al nuovo oggetto
Ad alcune persone piace var myFoo = new Foo();
leggere.
I dettagli dell'istanza vengono trapelati nell'API chiamante (tramite il new
requisito), quindi tutti i chiamanti sono strettamente associati all'implementazione del costruttore. Se hai mai bisogno dell'ulteriore flessibilità della fabbrica, dovrai riformattare tutti i chiamanti (è vero il caso eccezionale, piuttosto che la regola).
Dimenticare new
è un bug così comune, dovresti prendere in seria considerazione l'aggiunta di un controllo plateplate per assicurarti che il costruttore sia chiamato correttamente (if (!(this instanceof Foo)) { return new Foo() }
). EDIT: da ES6 (ES2015) non puoi dimenticare new
un class
costruttore, altrimenti il costruttore genererà un errore.
Se si esegue il instanceof
controllo, si lascia l'ambiguità sulla necessità o meno new
. Secondo me, non dovrebbe essere. Hai effettivamente cortocircuito ilnew
requisito, il che significa che potresti cancellare lo svantaggio n. 1. Ma poi hai appena una funzione di fabbrica in tutto tranne il nome , con ulteriore boilerplate, una lettera maiuscola e un this
contesto meno flessibile .
Ma la mia preoccupazione principale è che viola il principio aperto / chiuso. Inizi a esportare un costruttore, gli utenti iniziano a utilizzare il costruttore, quindi lungo la strada ti rendi conto che hai bisogno della flessibilità di una fabbrica, invece (ad esempio, per cambiare l'implementazione per utilizzare pool di oggetti o per creare un'istanza in contesti di esecuzione o per avere una maggiore flessibilità ereditaria utilizzando OO prototipo).
Sei bloccato, però. Non è possibile apportare la modifica senza rompere tutto il codice che chiama il costruttore connew
. Ad esempio, non è possibile passare all'utilizzo di pool di oggetti per migliorare le prestazioni.
Inoltre, l'uso di costruttori ti dà un inganno instanceof
che non funziona in contesti di esecuzione e non funziona se il tuo prototipo di costruttore viene scambiato. Fallirà anche se inizi a tornarethis
dal tuo costruttore e poi passi all'esportazione di un oggetto arbitrario, cosa che dovresti fare per abilitare un comportamento di fabbrica nel tuo costruttore.
Meno codice - nessuna caldaia richiesta.
Puoi restituire qualsiasi oggetto arbitrario e utilizzare qualsiasi prototipo arbitrario, offrendoti una maggiore flessibilità per creare vari tipi di oggetti che implementano la stessa API. Ad esempio, un lettore multimediale in grado di creare istanze sia di lettori HTML5 che di lettori flash o una libreria di eventi in grado di emettere eventi DOM o eventi di socket Web. Le fabbriche possono anche creare istanze di oggetti in contesti di esecuzione, sfruttare pool di oggetti e consentire modelli di ereditarietà prototipali più flessibili.
Non avresti mai bisogno di convertirti da una fabbrica a un costruttore, quindi il refactoring non sarà mai un problema.
Nessuna ambiguità sull'uso new
. Non farlo. (Farà this
comportarsi male, vedi punto successivo).
this
si comporta normalmente - quindi è possibile utilizzarlo per accedere all'oggetto padre (ad esempio, all'interno player.create()
, si this
riferisce player
, proprio come farebbe qualsiasi altro metodo di invocazione. call
E apply
anche riassegnare this
, come previsto. Se si memorizzano prototipi sull'oggetto padre, quello può essere un ottimo modo per scambiare dinamicamente funzionalità e abilitare un polimorfismo molto flessibile per l'istanza dell'oggetto.
Nessuna ambiguità sull'opportunità o meno di capitalizzare. Non farlo. Gli strumenti Lint si lamenteranno, quindi sarai tentato di provare a utilizzare new
e quindi annullerai i vantaggi sopra descritti.
Ad alcune persone piace il modo var myFoo = foo();
o var myFoo = foo.create();
legge.
new
non si comporta come previsto (vedi sopra). Soluzione: non usarlo.
this
non fa riferimento al nuovo oggetto (invece, se il costruttore viene invocato con notazione punto o notazione parentesi quadra, ad esempio foo.bar () - si this
riferisce a foo
- proprio come ogni altro metodo JavaScript - vedere i vantaggi).
new
viola il principio aperto / chiuso. Vedi medium.com/javascript-scene/… per una discussione molto più ampia di quella consentita da questi commenti.
new
parola chiave, non credo che la new
parola chiave in realtà fornisca alcuna leggibilità aggiuntiva. IMO, sembra sciocco saltare attraverso i cerchi per consentire ai chiamanti di digitare di più.
Un costruttore restituisce un'istanza della classe su cui lo chiami. Una funzione di fabbrica può restituire qualsiasi cosa. Si utilizzerà una funzione di fabbrica quando è necessario restituire valori arbitrari o quando una classe ha un processo di installazione di grandi dimensioni.
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Jack");
new
crea un oggetto prototipato User.prototype
e chiama User
con l'oggetto creato come this
valore.
new
considera un'espressione argomento per il suo operando come facoltativo:
let user = new User;
causerebbe new
chiamare User
senza argomenti.
new
restituisce l'oggetto che ha creato, a meno che il costruttore non restituisca un valore oggetto , che viene invece restituito. Questo è un caso limite che per la maggior parte può essere ignorato.
Gli oggetti creati dalle funzioni del costruttore ereditano le proprietà dalla proprietà del costruttore prototype
e restituiscono true utilizzando l' instanceOf
operatore sulla funzione del costruttore.
I comportamenti sopra riportati possono non riuscire se si modifica dinamicamente il valore della prototype
proprietà del costruttore dopo averlo già utilizzato. Ciò è raro e non può essere modificato se il costruttore è stato creato utilizzando la class
parola chiave.
Le funzioni del costruttore possono essere estese usando la extends
parola chiave.
Le funzioni del costruttore non possono essere restituite null
come valore di errore. Poiché non è un tipo di dati oggetto, viene ignorato da new
.
function User(name, age) {
return {
name,
age,
}
};
let user = User("Tom", 23);
Qui la funzione di fabbrica viene chiamata senza new
. La funzione è interamente responsabile dell'uso diretto o indiretto se restituisce i suoi argomenti e il tipo di oggetto. In questo esempio restituisce un semplice [oggetto oggetto] con alcune proprietà impostate dagli argomenti.
Nasconde facilmente le complessità di implementazione della creazione di oggetti dal chiamante. Ciò è particolarmente utile per le funzioni di codice nativo in un browser.
La funzione di fabbrica non deve sempre restituire oggetti dello stesso tipo e potrebbe persino tornare null
come indicatore di errore.
In casi semplici, le funzioni di fabbrica possono essere semplici nella struttura e nel significato.
Gli oggetti restituiti generalmente non ereditano dalla prototype
proprietà della funzione di fabbrica e restituiscono false
da instanceOf factoryFunction
.
La funzione factory non può essere estesa in modo sicuro utilizzando la extends
parola chiave perché gli oggetti estesi erediteranno dalla prototype
proprietà delle funzioni di fabbrica anziché dalla prototype
proprietà del costruttore utilizzata dalla funzione di fabbrica.
Le fabbriche sono "sempre" migliori. Quindi, quando si usano lingue orientate agli oggetti
Le implementazioni (gli oggetti reali creati con nuovi) non sono esposte all'utente / consumatore della fabbrica. Ciò significa che lo sviluppatore della fabbrica può espandere e creare nuove implementazioni purché non violi il contratto ... e consente al consumatore della fabbrica di beneficiare della nuova API senza dover modificare il proprio codice ... se hanno usato una nuova e una "nuova" implementazione, allora devono andare e cambiare ogni riga che usa "nuova" per usare la "nuova" implementazione ... con la fabbrica il loro codice non cambia ...
Fabbriche - meglio di ogni altra cosa - la struttura a molla è completamente costruita attorno a questa idea.
Le fabbriche sono uno strato di astrazione e come tutte le astrazioni hanno un costo in complessità. Quando si incontra un'API basata sulla fabbrica, capire quale sia la fabbrica per una determinata API può essere una sfida per il consumatore dell'API. Con i costruttori la rilevabilità è banale.
Quando si decide tra medici e fabbriche è necessario decidere se la complessità è giustificata dal beneficio.
Vale la pena notare che i costruttori Javascript possono essere fabbriche arbitrarie restituendo qualcosa di diverso da questo o indefinito. Quindi in js puoi ottenere il meglio da entrambi i mondi: API rilevabili e pool di oggetti / cache.
new
, Modifica del comportamento di this
, Modifica del valore di ritorno, Connessione di un riferimento prototipo, Abilitazione instanceof
(che si trova e non deve essere utilizzata per questo scopo). Apparentemente, tutte queste sono "caratteristiche". In pratica, danneggiano la qualità del tuo codice.