Funkcja konstruktora a funkcje fabryczne

Czy ktoś może wyjaśnić różnicę między funkcją konstruktora a funkcją fabryczną w Javascript.

Kiedy używać jednego zamiast drugiego?

Author: Sinan, 2012-01-02

7 answers

Podstawowa różnica polega na tym, że funkcja konstruktora jest używana ze słowem kluczowym new (co powoduje, że JavaScript automatycznie tworzy nowy obiekt, ustawia this wewnątrz funkcji do tego obiektu i zwraca obiekt):

var objFromConstructor = new ConstructorFunction();

Funkcja fabryczna jest nazywana jak funkcja "regularna":

var objFromFactory = factoryFunction();

Ale aby była uważana za "fabryczną", musiałaby zwrócić nową instancję jakiegoś obiektu: nie nazwałbyś jej funkcją" fabryczną", gdyby po prostu zwróciła wartość logiczną czy coś takiego. Nie dzieje się to automatycznie, jak w przypadku new, ale pozwala to na większą elastyczność w niektórych przypadkach.

W bardzo 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żna znacznie bardziej skomplikować funkcje fabryczne niż ten prosty przykład.

Jedną z zalet funkcji fabrycznych jest to, że zwracany obiekt może być kilku różnych typów w zależności od jakiegoś parametru.

 161
Author: nnnnnn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-15 01:30:41

Korzyści z używania konstruktorów

  • Większość książek uczy używania konstruktorów i new

  • this odnosi się do nowego obiektu

  • Niektórym podoba się sposób czytania.

Wady

  • Szczegóły instancji są wyciekające do wywołującego API (poprzez wymóg new), więc wszystkie wywołujące są ściśle powiązane z implementacją konstruktora. Jeśli kiedykolwiek potrzebujesz dodatkowej elastyczności fabrycznie, trzeba będzie refaktorować wszystkich rozmówców (co prawda wyjątkowego przypadku, a nie reguły).

  • Zapominanie new jest tak częstym błędem, powinieneś zdecydowanie rozważyć dodanie sprawdzania boilerplate, aby upewnić się, że konstruktor jest poprawnie wywołany ( if (!(this instanceof Foo)) { return new Foo() } ). EDIT: od ES6 (ES2015) nie można zapomnieć new z konstruktorem class, albo konstruktor wyrzuci błąd.

  • Jeśli wykonasz instanceof sprawdzenie, pozostawia niejasność, czy new jest wymagane. Moim zdaniem nie powinno być. Skutecznie zwarłeś new wymóg, co oznacza, że możesz usunąć wadę # 1. Ale wtedy masz tylko funkcję fabryczną we wszystkich poza nazwą, z dodatkową płytą kotła, wielką literą i mniej elastycznym kontekstem this.

Konstruktory przełamują zasadę otwartą / zamkniętą

Ale moim głównym zmartwieniem jest to, że narusza to zasadę otwartą / zamkniętą. Zaczynasz eksportować konstruktor, użytkownicy zacznij używać konstruktora, a potem zdasz sobie sprawę, że potrzebujesz elastyczności fabryki (na przykład, aby zmienić implementację, aby użyć puli obiektów, utworzyć instancję w różnych kontekstach wykonawczych lub mieć większą elastyczność dziedziczenia przy użyciu prototypowego OO).

Ale utknąłeś. Nie możesz dokonać zmiany bez złamania całego kodu, który wywołuje twój konstruktor za pomocą new. Nie można przełączyć się na korzystanie z pul obiektów w celu zwiększenia wydajności, dla przykład.

Ponadto używanie konstruktorów daje zwodniczą instanceof, która nie działa w kontekstach wykonawczych i nie działa, jeśli prototyp konstruktora zostanie zamieniony. Nie powiedzie się również, jeśli zaczniesz zwracać this z konstruktora, a następnie przełączysz się na eksport dowolnego obiektu, co musisz zrobić, aby włączyć zachowanie fabryczne w konstruktorze.

Korzyści z korzystania z fabryk

  • Less code-no boilerplate wymagane.

  • Możesz zwrócić dowolny dowolny obiekt i użyć dowolnego prototypu-co daje większą elastyczność w tworzeniu różnych typów obiektów, które implementują ten sam API. Na przykład odtwarzacz multimedialny, który może tworzyć instancje odtwarzaczy HTML5 i flash, lub biblioteka zdarzeń, która może emitować zdarzenia DOM lub zdarzenia gniazda sieciowego. Fabryki mogą również tworzyć instancje obiektów w różnych kontekstach wykonania, korzystać z pul obiektów i umożliwiać bardziej elastyczne prototypowanie modele dziedziczenia.

  • Nigdy nie będzie potrzeby konwersji z fabryki na konstruktora, więc refaktoryzacja nigdy nie będzie problemem.

  • Brak niejasności co do używania new. Nie. (to sprawi, że this będzie się źle zachowywać, patrz następny punkt).

  • this zachowuje się tak, jak normalnie - więc możesz go użyć, aby uzyskać dostęp do obiektu nadrzędnego (na przykład wewnątrz player.create(), this odnosi się do player, tak jak każde inne wywołanie metody. call i apply także resign this, zgodnie z oczekiwaniami. Jeśli przechowujesz prototypy w obiekcie nadrzędnym, może to być świetny sposób na dynamiczną wymianę funkcjonalności i włączenie bardzo elastycznego polimorfizmu dla instancji obiektu.

  • Nie ma niejasności, czy kapitalizować. Nie. narzędzia Lint będą narzekać, a potem będziesz kuszony, aby spróbować użyć new, a następnie cofniesz opisaną powyżej korzyść.

  • Niektórzy ludzie lubią drogę var myFoo = foo(); lub var myFoo = foo.create(); czyta.

Wady

  • new nie zachowuje się zgodnie z oczekiwaniami(patrz wyżej). Rozwiązanie: nie używaj go.

  • this nie odnosi się do nowego obiektu (zamiast tego, jeśli konstruktor jest wywoływany z notacją kropkową lub nawiasem kwadratowym, np. foo.bar () - this odnosi się do foo - podobnie jak każda inna metoda JavaScript -- patrz korzyści).

 120
Author: Eric Elliott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-24 18:34:38

Konstruktor zwraca instancję klasy, do której ją wywołujesz. Funkcja fabryczna może zwrócić wszystko. Można użyć funkcji fabrycznej, gdy trzeba zwrócić dowolne wartości lub gdy klasa ma duży proces konfiguracji.

 40
Author: Ignacio Vazquez-Abrams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-02 08:24:44

Przykład funkcji konstruktora

function User(name) {
  this.name = name;
  this.isAdmin = false;
}

let user = new User("Jack");
  • new tworzy prototyp obiektu User.prototype i wywołuje User z wytworzonym obiektem jako jego wartością this.

  • new traktuje wyrażenie argumentu dla jego operandu jako opcjonalne:

         let user = new User;
    

    Spowoduje wywołanie new bez argumentów.

  • new zwraca utworzony obiekt , chyba że konstruktor Zwraca wartość obiektu, która jest zwracana w zamian. To jest wielkość krawędzi, która w większości przypadków może być ignorowana.

Plusy i minusy

Obiekty utworzone przez funkcje konstruktora dziedziczą właściwości z właściwości konstruktora prototype i zwracają true używając operatora instanceOf w funkcji konstruktora.

Powyższe zachowania mogą się nie udać, jeśli dynamicznie zmienisz wartość właściwości prototype konstruktora po użyciu konstruktora. Jest to rzadkość i nie można tego zmienić, jeśli konstruktory zostały utworzone przy użyciu słowa kluczowego class.

Funkcje konstruktora można rozszerzyć za pomocą słowa kluczowego extends.

Funkcje konstruktora nie mogą zwracać null jako wartości błędu. Ponieważ nie jest to obiektowy typ danych, jest ignorowany przez new.

Przykład funkcji Fabrycznej

function User(name, age) {
  return {
    name,
    age,
  }
};

let user = User("Tom", 23);

Tutaj funkcja fabryczna jest wywoływana 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ład zwraca prosty obiekt [Object] z niektórymi właściwościami ustawionymi z argumentów.

Plusy i minusy

Łatwo ukrywa złożoność implementacji tworzenia obiektów przed wywołującym. Jest to szczególnie przydatne w przypadku funkcji kodu natywnego w przeglądarce.

Funkcja factory 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ą być proste w budowie i to znaczy.

Zwracane obiekty na ogół nie dziedziczą właściwości prototype funkcji fabrycznej i zwracają false z instanceOf factoryFunction.

Funkcja factory nie może być bezpiecznie rozszerzona za pomocą słowa kluczowego extends, ponieważ rozszerzone obiekty dziedziczyłyby z właściwości factory functions prototype zamiast z właściwości prototype konstruktora używanego przez funkcję factory.

 8
Author: traktor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Fabryki są "zawsze" lepsze. Jeśli używasz języków zorientowanych obiektowo, to

  1. zdecyduj o umowie (metody i co zrobią)
  2. tworzenie interfejsów, które ujawniają te metody (w javascript nie masz interfejsów, więc musisz wymyślić jakiś sposób sprawdzenia implementacji)
  3. Utwórz fabrykę, która zwróci implementację każdego wymaganego interfejsu.

Implementacje (rzeczywiste obiekty utworzone z new) nie są eksponowane użytkownikowi/konsumentowi fabrycznemu. Oznacza to, że deweloper fabryki może rozbudowywać i tworzyć nowe wdrożenia, o ile nie zerwie umowy...pozwala to fabrycznemu konsumentowi po prostu korzystać z nowego API bez konieczności zmiany kodu...jeśli użyli nowej i pojawi się "nowa" implementacja, to muszą przejść i zmienić każdą linię, która używa" nowej", aby użyć" nowej " implementacji...z fabryką ich Kod się nie zmienia...

Fabryki-lepsze niż Wszystko inne-spring framework jest całkowicie zbudowany wokół tego pomysłu.

 3
Author: Colin Saxton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-09 11:14:38

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 danego interfejsu API, może być wyzwaniem dla konsumenta interfejsu API. Z konstruktorami odkrywalność jest trywialna.

Decydując się między ctors a fabrykami, musisz zdecydować, czy złożoność jest uzasadniona korzyścią.

Warto zauważyć, że konstruktory Javascript mogą być dowolne, zwracając coś innego niż to lub nieokreślone. Tak więc w js możesz uzyskać to, co najlepsze z obu światów-wykrywalne API i pooling / buforowanie obiektów.

 0
Author: PeterH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-07 02:52:42

Dla różnic, Eric Elliott wyjaśnił bardzo dobrze,

Ale na drugie pytanie:

Kiedy używać jednego zamiast drugiego?

Jeśli pochodzisz z tła zorientowanego obiektowo, funkcja konstruktora wygląda dla ciebie bardziej naturalnie. w ten sposób nie zapomnij użyć słowa kluczowego new.

 0
Author: Mostafa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-16 11:34:00