Czy ktoś może wyjaśnić różnicę między funkcją konstruktora a funkcją fabryczną w JavaScript.
Kiedy używać jednego zamiast drugiego?
źródło
Czy ktoś może wyjaśnić różnicę między funkcją konstruktora a funkcją fabryczną w JavaScript.
Kiedy używać jednego zamiast drugiego?
Podstawowa różnica polega na tym, że funkcja konstruktora jest używana ze new
słowem kluczowym (co powoduje, że JavaScript automatycznie tworzy nowy obiekt, ustawia this
w funkcji na ten obiekt i zwraca obiekt):
var objFromConstructor = new ConstructorFunction();
Funkcja fabryczna nazywana jest „zwykłą” funkcją:
var objFromFactory = factoryFunction();
Ale aby można było uznać ją za „fabrykę”, musiałby zwrócić nową instancję jakiegoś obiektu: nie nazwałbyś jej funkcją „fabryczną”, gdyby po prostu zwracała wartość logiczną lub coś w tym rodzaju. Nie dzieje się to automatycznie, jak w przypadku new
, ale w niektórych przypadkach pozwala na większą elastyczność.
W naprawdę prostym przykładzie funkcje wymienione powyżej mogą wyglądać mniej więcej tak:
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;
}
Oczywiście możesz znacznie bardziej skomplikować funkcje fabryczne niż ten prosty przykład.
Jedną z zalet funkcji fabrycznych jest sytuacja, gdy zwracany obiekt może mieć kilka różnych typów w zależności od niektórych parametrów.
someMethod
obiektów zwróconych przez fabrykę i wtedy robi się trochę mglisty. Wewnątrz funkcji fabrycznej, jeśli tylko to zrobivar obj = { ... , someMethod: function() {}, ... }
, doprowadzi to do tego, że każdy zwracany obiekt będzie zawierał inną kopię,someMethod
której moglibyśmy nie chcieć. W tym miejscu pomogłoby użycienew
iprototype
wewnątrz funkcji fabrycznej.new
funkcji konstruktora; Pomyślałem, że w tym miejscu może zaistnieć potrzeba zastąpienia konstruktorów przykładem funkcji fabrycznych i pomyślałem, że wymagana jest spójność przykładów. W każdym razie odpowiedź jest wystarczająco pouczająca. To była tylko kwestia, którą chciałem poruszyć, a nie to, że w jakikolwiek sposób obniżam jakość odpowiedzi.new
wewnętrznie lub używaćObject.create()
do tworzenia obiektu z określonym prototypem.Korzyści z używania konstruktorów
Większość książek uczy, jak używać konstruktorów i
new
this
odnosi się do nowego obiektuNiektórzy ludzie lubią sposób
var myFoo = new Foo();
czytać.Wady
Szczegóły tworzenia wystąpień wyciekają do wywołującego interfejsu API (poprzez
new
wymaganie), więc wszystkie wywołujące są ściśle powiązane z implementacją konstruktora. Jeśli kiedykolwiek będziesz potrzebować dodatkowej elastyczności fabryki, będziesz musiał refaktoryzować wszystkich dzwoniących (trzeba przyznać, że jest to wyjątkowy przypadek, a nie reguła).Zapominanie
new
jest tak częstym błędem, należy zdecydowanie rozważyć dodanie standardowego sprawdzenia, aby upewnić się, że konstruktor jest wywoływany poprawnie (if (!(this instanceof Foo)) { return new Foo() }
). EDYCJA: Od ES6 (ES2015) nie można zapomniećnew
oclass
konstruktorze, w przeciwnym razie konstruktor zgłosi błąd.Jeśli to zrobisz
instanceof
, pozostawia to niejasność co do tego, czynew
jest to wymagane. Moim zdaniem nie powinno. Skutecznie zwarłeśnew
wymagania, co oznacza, że możesz usunąć wadę # 1. Ale masz po prostu funkcję fabryki , z wyjątkiem nazwy , z dodatkowym schematem, wielką literą i mniej elastycznymthis
kontekstem.Konstruktorzy łamią zasadę otwartej / zamkniętej
Ale moim głównym zmartwieniem jest to, że narusza zasadę otwartego / zamkniętego. Zaczynasz od eksportowania konstruktora, użytkownicy zaczynają używać konstruktora, a następnie po drodze zdajesz sobie sprawę, że zamiast tego potrzebujesz elastyczności fabryki (na przykład, aby przełączyć implementację na używanie pul obiektów, tworzenie instancji w kontekstach wykonania lub mają większą elastyczność dziedziczenia dzięki prototypowemu obiektowi zewnętrznemu).
Ale utkniesz. Nie możesz dokonać zmiany bez zerwania całego kodu, który wywołuje twój konstruktor
new
. Na przykład nie można przełączyć się na używanie pul obiektów w celu zwiększenia wydajności.Ponadto używanie konstruktorów daje oszustwo,
instanceof
które nie działa w różnych kontekstach wykonania i nie działa, jeśli twój prototyp konstruktora zostanie wymieniony. Nie powiedzie się również, jeśli zaczniesz wracaćthis
z konstruktora, a następnie przełączysz się na eksportowanie dowolnego obiektu, co musisz zrobić, aby włączyć zachowanie fabryczne w konstruktorze.Korzyści z używania fabryk
Mniej kodu - nie jest wymagany szablon.
Możesz zwrócić dowolny obiekt i użyć dowolnego prototypu - co daje większą elastyczność przy tworzeniu różnych typów obiektów, które implementują to samo API. Na przykład odtwarzacz multimedialny, który może tworzyć instancje odtwarzaczy HTML5 i Flash, lub bibliotekę zdarzeń, która może emitować zdarzenia DOM lub zdarzenia gniazd sieciowych. Fabryki mogą również tworzyć instancje obiektów w kontekstach wykonywania, wykorzystywać pule obiektów i pozwalać na bardziej elastyczne prototypowe modele dziedziczenia.
Nigdy nie musiałbyś przechodzić z fabryki na konstruktora, więc refaktoryzacja nigdy nie będzie problemem.
Brak dwuznaczności w używaniu
new
. Nie. (Spowoduje tothis
złe zachowanie, patrz następny punkt).this
zachowuje się jak zazwyczaj - tak można go użyć do uzyskania dostępu do obiektu nadrzędnego (na przykład wewnątrzplayer.create()
,this
odnosi się doplayer
., tak jak każdy inny wywołania metoda byłabycall
iapply
również Przypiszthis
, zgodnie z oczekiwaniami Jeśli przechowujesz prototypy na obiekcie nadrzędnym, że. może być świetnym sposobem na dynamiczną wymianę funkcjonalności i włączenie bardzo elastycznego polimorfizmu do tworzenia instancji obiektu.Brak dwuznaczności co do tego, czy kapitalizować. Nie. Narzędzia kłaczków będą narzekać, a wtedy będziesz kuszony, aby spróbować ich użyć
new
, a wtedy cofniesz korzyści opisane powyżej.Niektórzy ludzie lubią sposób
var myFoo = foo();
lubvar myFoo = foo.create();
czyta.Wady
new
nie zachowuje się zgodnie z oczekiwaniami (patrz wyżej). Rozwiązanie: nie używaj go.this
nie odwołuje się do nowego obiektu (zamiast tego, jeśli konstruktor jest wywoływany z notacją z kropką lub nawiasem kwadratowym, np. foo.bar () -this
odnosi sięfoo
- jak każda inna metoda JavaScript - zobacz korzyści).źródło
new
narusza zasadę otwarcia / zamknięcia. Zajrzyj na medium.com/javascript-scene/…, aby uzyskać dużo szerszą dyskusję, niż pozwalają na to te komentarze.new
słowa kluczowego, nie sądzę, abynew
słowo kluczowe faktycznie zapewniało jakąkolwiek dodatkową czytelność. IMO, wydaje się głupie przeskakiwanie przez obręcze, aby umożliwić dzwoniącym pisanie więcej.Konstruktor zwraca instancję klasy, do której został wywołany. Funkcja fabryczna może zwrócić wszystko. Funkcji fabrycznej można użyć, gdy trzeba zwrócić dowolne wartości lub gdy klasa ma duży proces konfiguracji.
źródło
Przykład funkcji konstruktora
new
tworzy prototyp obiektuUser.prototype
i wywołujeUser
utworzony obiekt jako jegothis
wartość.new
traktuje wyrażenie argumentu dla swojego operandu jako opcjonalne:spowodowałoby
new
wywołanieUser
bez argumentów.new
zwraca utworzony przez siebie obiekt, chyba że konstruktor zwraca wartość obiektu , która jest zamiast tego zwracana. Jest to skrajny przypadek, który w większości można zignorować.Plusy i minusy
Obiekty utworzone przez funkcje konstruktora dziedziczą właściwości z właściwości konstruktora
prototype
i zwracają wartość true przy użyciuinstanceOf
operatora funkcji konstruktora.Powyższe zachowania mogą się nie powieść, jeśli dynamicznie zmienisz wartość właściwości konstruktora
prototype
po wcześniejszym użyciu konstruktora. Jest to rzadkie i nie można go zmienić, jeśli konstruktor został utworzony przy użyciuclass
słowa kluczowego.Funkcje konstruktora można rozszerzyć za pomocą
extends
słowa kluczowego.Funkcje konstruktora nie mogą zwracać
null
wartości błędu. Ponieważ nie jest to typ danych obiektowych, jest ignorowany przeznew
.Przykład funkcji Factory
Tutaj wywoływana jest funkcja fabryczna bez
new
. Funkcja jest całkowicie odpowiedzialna za bezpośrednie lub pośrednie użycie, jeśli jej argumenty i typ zwracanego obiektu. W tym przykładzie zwraca prosty [obiekt Object] z niektórymi właściwościami ustawionymi na podstawie argumentów.Plusy i minusy
Z łatwością ukrywa przed wywołującym złożoność implementacji tworzenia obiektów. Jest to szczególnie przydatne w przypadku natywnych funkcji kodu w przeglądarce.
Funkcja fabryczna nie zawsze musi zwracać obiekty tego samego typu, a może nawet zwracać
null
jako wskaźnik błędu.W prostych przypadkach funkcje fabryczne mogą mieć prostą strukturę i znaczenie.
Zwracane obiekty zazwyczaj nie dziedziczą z właściwości funkcji fabryki
prototype
i wracająfalse
zinstanceOf factoryFunction
.Funkcji fabrycznej nie można bezpiecznie rozszerzyć za pomocą
extends
słowa kluczowego, ponieważ rozszerzone obiekty będą dziedziczyć zprototype
właściwości funkcji fabryki, a nie zprototype
właściwości konstruktora używanego przez funkcję fabryki.źródło
Fabryki są „zawsze” lepsze. W takim razie podczas używania języków obiektowych
Implementacje (rzeczywiste obiekty utworzone za pomocą nowego) nie są ujawniane fabrycznemu użytkownikowi / konsumentowi. Oznacza to, że deweloper fabryki może rozwijać i tworzyć nowe implementacje, o ile nie zerwie umowy ... i pozwala fabrycznemu konsumentowi po prostu korzystać z nowego interfejsu API bez konieczności zmiany kodu ... jeśli użyli nowej i pojawiła się „nowa” implementacja, muszą przejść i zmienić każdą linię, która używa „nowej”, aby użyć „nowej” implementacji ... w fabryce ich kod się nie zmienia ...
Fabryki - lepsze niż wszystko inne - ramy wiosenne są całkowicie zbudowane wokół tego pomysłu.
źródło
Fabryki są warstwą abstrakcji i jak wszystkie abstrakcje mają koszt w złożoności. W przypadku napotkania fabrycznego interfejsu API ustalenie, jaka jest fabryka dla danego interfejsu API, może stanowić wyzwanie dla konsumenta API. W przypadku konstruktorów wykrywalność jest banalna.
Decydując się między lekarzami a fabrykami, musisz zdecydować, czy złożoność jest uzasadniona korzyścią.
Warto zauważyć, że konstruktory Javascript mogą być dowolnymi fabrykami, zwracając coś innego niż this lub undefined. Tak więc w js możesz uzyskać to, co najlepsze z obu światów - wykrywalny interfejs API i buforowanie / buforowanie obiektów.
źródło
new
, zmianę zachowaniathis
, zmianę wartości zwracanej, podłączenie referencji prototypu, włączanieinstanceof
(co leży i nie powinno być używane do tego celu). Na pozór wszystko to są „funkcje”. W praktyce szkodzą one jakości kodu.Eric Elliott wyjaśnił bardzo dobrze różnice,
Ale jeśli chodzi o drugie pytanie:
Jeśli pochodzisz z tła zorientowanego obiektowo, funkcja Konstruktor wygląda dla Ciebie bardziej naturalnie. w ten sposób nie zapomnij użyć
new
słowa kluczowego.źródło