Wzorce obsługi zdarzeń w JavaScript. Do dalszej lektury

W tym artykule omówimy typowe wzorce projektowe w JS. Wzorce te oferują programistom sposoby rozwiązywania problemów technicznych w elegancki sposób, który można ponownie wykorzystać. Chcesz udoskonalić swoje umiejętności JavaScript? Następnie czytaj dalej.

Co to jest wzór lub wzór projektowy?

Mówiąc najprościej, wzorzec projektowy to rozwiązanie programowe wielokrotnego użytku służące do rozwiązywania określonego rodzaju problemu, który często pojawia się podczas tworzenia oprogramowania. W ciągu wielu lat praktyki tworzenia oprogramowania eksperci wymyślili sposoby rozwiązania takich problemów. Decyzje te zostały ujęte we wzorce projektowe.

  • szablony są sprawdzonymi rozwiązaniami problemów związanych z tworzeniem oprogramowania
  • szablony są skalowalne, ponieważ zazwyczaj mają strukturę i zasady, których należy przestrzegać
  • szablony można ponownie wykorzystać do podobnych zadań

Rodzaje wzorców projektowych

Podczas tworzenia oprogramowania wzorce projektowe dzieli się zwykle na kilka kategorii. W tym artykule przyjrzymy się trzem najważniejszym.

Krótko o nich:

  • Wzorce kreacyjne skupiają się na sposobie tworzenia obiektów lub klas. Może się to wydawać proste (i w niektórych przypadkach takie jest), ale duże aplikacje muszą kontrolować proces tworzenia obiektów.
  • Wzorce projektowania strukturalnego koncentrują się na zarządzaniu relacjami między obiektami, dzięki czemu aplikacja jest budowana w sposób skalowalny. Kluczowym aspektem modelu konstrukcyjnego jest zapewnienie, że zmiana w jednej części aplikacji nie wpłynie na wszystkie inne części.
  • Wzorce behawioralne koncentrują się na połączeniach między obiektami
  • Uwaga dotycząca klas w JavaScript

    Czytając o wzorcach projektowych, często można spotkać odniesienia do klas i obiektów. Może to być mylące, ponieważ JavaScript tak naprawdę nie używa „klasy”; bardziej poprawnym terminem jest „typ danych”.

    Typy danych JavaScript

    JavaScript to język zorientowany obiektowo, w którym obiekty dziedziczą od innych obiektów, zgodnie z koncepcją znaną jako dziedziczenie prototypowe. Typy danych można tworzyć poprzez zdefiniowanie tak zwanej „funkcji konstruktora”, na przykład:

    Funkcja Osoba(config) ( this.name = nazwa.konfiguracji; this.age = config.age; ) Person.prototype.getAge = funkcja() ( return this.age; ); var tilo = nowa osoba((imię: „Tilo”, wiek: 23 )); konsola.log(tilo.getAge());

    Zwróć uwagę na użycie prototype przy definiowaniu metod dla typu danych Osoba. Ponieważ wiele obiektów Person będzie odwoływać się do tego samego prototypu, umożliwi to współużytkowanie metody getAge() przez wszystkie instancje typu danych Person zamiast zastępowania jej dla poszczególnych instancji. Dodatkowo każdy typ danych dziedziczący po Person będzie miał dostęp do metody getAge().

    Praca z prywatnością

    Innym częstym problemem związanym z JavaScriptem jest to, że nie ma on żadnych zmiennych prywatnych. Możemy jednak użyć wielu zamknięć, aby symulować prywatność. Rozważ następujący fragment:

    Var retinaMacbook = (funkcja() ( //Zmienne prywatne var RAM, addRAM; RAM = 4; //Metody prywatne addRAM = funkcja (dodatkowaRAM) ( RAM += dodatkowaRAM; ); return ( //Zmienne publiczne i metody USB: niezdefiniowane , wstawUSB: funkcja (urządzenie) ( this.USB = urządzenie; ), usuńUSB: funkcja () ( var urządzenie = this.USB; this.USB = niezdefiniowane; zwróć urządzenie; ) ); ))();

    W powyższym przykładzie utworzyliśmy obiekt retinaMacbook ze zmiennymi i metodami publicznymi i prywatnymi. Oto jak go wykorzystamy:

    RetinaMacbook.insertUSB("myUSB"); konsola.log(retinaMacbook.USB); //wylogowuje "myUSB" console.log(retinaMacbook.RAM) //wylogowuje się niezdefiniowany

    Wzorce generatywne

    Istnieje wiele różnych rodzajów wzorców spawnowania (fabryka abstrakcyjna, konstruktor, metoda fabryczna, leniwa inicjalizacja, pula singletonu, pula obiektów, prototyp, pobieranie zasobu to inicjalizacja, singleton), ale w tym samouczku przyjrzymy się tylko dwóm z nich nich: Konstruktor i Prototyp. Są używane wystarczająco często, aby zwrócić na siebie uwagę.

    Wzór budowniczego

    Wzorzec budowania jest często używany przy tworzeniu stron internetowych i prawdopodobnie używałeś go już wcześniej, nie zdając sobie z tego sprawy. Mówiąc najprościej, wzór ten można zdefiniować w następujący sposób:

    Korzystanie z szablonu konstrukcyjnego umożliwia budowanie obiektów poprzez określenie jedynie typu i zawartości obiektu. Nie ma potrzeby tworzenia obiektu.

    Na przykład prawdopodobnie robiłeś to niezliczoną ilość razy w jQuery:

    Var myDiv = $("To jest div."); //myDiv reprezentuje teraz obiekt jQuery odwołujący się do węzła DOM. var jakiś tekst = $("

    "); //someText jest obiektem jQuery odwołującym się do elementu HTMLParagraphElement var input = $("");

    Spójrz na trzy powyższe przykłady. W pierwszym przeszliśmy do elementu z pewną treścią. W drugim udaliśmy się do pustego tagu

    W tym ostatnim udaliśmy się do żywiołu. Wynik wszystkich trzech był taki sam - zwrócono nam obiekt jQuery odwołujący się do węzła DOM.

    Zmienna $ dostosowuje wzorzec budowania jQuery. W każdym przykładzie otrzymaliśmy obiekt jQuery DOM i mieliśmy dostęp do wszystkich metod udostępnianych przez bibliotekę jQuery i w żadnym momencie nie wywoływaliśmy document.createElement. Biblioteka JS zajmowała się tym wszystkim za zamkniętymi drzwiami.

    Wyobraź sobie, ile pracy wymagałoby utworzenie elementu DOM i wstawienie do niego treści! Stosując wzorzec konstrukcyjny, możemy skupić uwagę na rodzaju i treści obiektu, a nie na jego jawnym kreowaniu.

    Wzór prototypowy

    Wcześniej przyglądaliśmy się procesowi definiowania typów danych w JavaScript poprzez funkcje i dodawanie metod do prototypu obiektu. Wzorce prototypowe (schematy) umożliwiają obiektom dziedziczenie od innych obiektów poprzez ich prototypy.

    Szablon prototypowy to szablon, w którym obiekty są tworzone na podstawie szablonu istniejącego obiektu poprzez klonowanie.

    Jest to prosty i naturalny sposób implementacji dziedziczenia w JavaScript. Na przykład:

    Var Osoba = (numFeet: 2, numHeads: 1, numHands:2); //Object.create pobiera swój pierwszy argument i stosuje go do prototypu nowego obiektu. var tilo = Obiekt.utwórz(Osoba); konsola.log(tilo.numHeads); //wynik 1 tilo.numHeads = 2; console.log(tilo.numHeads) //wynik 2

    Właściwości (i metody) obiektu Person są stosowane do prototypu obiektu tilo. Możemy zastąpić właściwości obiektu tilo, jeśli chcemy, aby były inne.

    W powyższym przykładzie użyliśmy Object.create(). Jednak przeglądarka Internet Explorer 8 nie obsługuje nowej metody. W takich przypadkach możemy naśladować jego zachowanie:

    Var VehiclePrototype = ( init: funkcja (carModel) ( this.model = carModel; ), getModel: funkcja () ( console.log("Model tego pojazdu to " + this.model); ) ); funkcja pojazd (model) ( funkcja F() (); F.prototype = VehiclePrototype; var f = nowy F(); f.init(model); return f; ) var samochód = pojazd("Ford Escort"); samochód.getModel();

    Wzory strukturalne

    Wzorce strukturalne są naprawdę przydatne, szczególnie przy ustalaniu, jak system powinien działać. Umożliwiają łatwe skalowanie aplikacji i zarządzanie nimi. Z tej znacznej grupy (adapter, most, łącznik, dekorator, fasada, pojedynczy punkt wejścia, oportunista, proxy) rozważymy następujące modele: Composer i Facade.

    Linker (wzorzec projektowy)

    • Wzorzec Composer to inny typ wzorca, z którego prawdopodobnie korzystałeś, nie zdając sobie z tego sprawy.

    Co to oznacza? Rzućmy okiem na następujący przykład w jQuery (większość bibliotek JS będzie miała odpowiedniki tego):

    $(.myList").addClass("wybrane"); $("#myItem").addClass("wybrane"); //Nie rób tego na dużych stołach, to tylko przykład. $("#dataTable tbody tr").on("kliknięcie", funkcja(event)( alert($(this).text()); )); $("#myButton").on("kliknięcie", funkcja(zdarzenie) ( alert("Kliknięty."); ));

    Większość Biblioteki JavaScriptu zapewnić spójne API, niezależnie od tego, czy mamy do czynienia z pojedynczym elementem DOM, czy tablicą elementów DOM. W pierwszym przykładzie możemy dodać wybraną klasę do wszystkich elementów pasujących do selektora .myList, ale tę samą metodę możemy zastosować także w przypadku pojedynczego elementu DOM, czyli #myItem. Podobnie można dołączyć moduł obsługi zdarzeń za pomocą metody on() na wielu węzłach lub na pojedynczym węźle za pośrednictwem tego samego interfejsu API.

    Korzystając z układów złożonych, jQuery (i wiele innych bibliotek) zapewnia nam uproszczone API.

    Szablon złożony może czasami powodować problemy. W luźno zakodowanym języku, takim jak JavaScript, warto wiedzieć, czy mamy do czynienia z pojedynczym elementem, czy wieloma elementami. Ponieważ kreator szablonów używa tego samego interfejsu API w obu przypadkach, często możemy pomylić jedno z drugim, co kończy się nieoczekiwanym błędem. Niektóre biblioteki, takie jak YUI3, oferują dwie oddzielne metody pobierania elementów (Y.one() vs Y.all()).

    Fasada (wzór projektowy)

    Oto kolejny wzór, który uważamy za oczywisty. Obiekt abstrahujący dzieło od kilku klas, łącząc je w jedną całość.

    Wzór elewacji zapewnia użytkownikowi prosty interfejs, ukrywając jego główne zawiłości.

    Wzór fasady prawie zawsze poprawia użyteczność oprogramowania. Na przykładzie jQuery jedną z najpopularniejszych metod bibliotecznych jest metoda Ready():

    $(document).ready(function() ( //tutaj trafia cały kod... ));

    Metoda Ready() faktycznie implementuje fasadę. Jeśli zajrzysz do źródła, oto co znajdziesz:

    Gotowy: (function() ( ... //Mozilla, Opera i Webkit if (document.addEventListener) ( document.addEventListener("DOMContentLoaded", idempotent_fn, false); ... ) //Model zdarzeń IE else if ( document.attachEvent) ( // upewnij się, że zostanie uruchomione przed załadowaniem; może być opóźnione, ale bezpieczne dla ramek iframe document.attachEvent("onreadystatechange", idempotent_fn); // Rozwiązanie awaryjne dla window.onload, które zawsze działa window.attachEvent("onload", idempotent_fn); ... ) ))

    Metoda Ready() nie jest taka prosta. jQuery normalizuje zmienność przeglądarki, dzięki czemu funkcja Read() uruchamia się we właściwym czasie. Jednak jako programista otrzymasz prosty interfejs.

    Większość przykładów szablonów fasad jest zgodna z tą zasadą. Aby go wdrożyć, zwykle polegamy Instrukcje warunkowe, ale przedstawiamy to w formie prosty interfejs dla użytkownika. Inne metody implementacji tego wzorca obejmują animate() i css().

    Wzorce zachowań

    Każdy obiektowy system oprogramowania będzie zawierał relacje między obiektami. Brak organizacji takich połączeń może prowadzić do błędów trudnych do znalezienia i skorygowania. Zalecane są wzorce projektowania zachowań różne metody organizowanie komunikacji pomiędzy obiektami (łańcuch odpowiedzialności, dowodzenie, interpreter, iterator, mediator, opiekun, obserwator, sługa, specyfikacja, stan, strategia, metoda szablonowa, gość itp.). W tej sekcji przyjrzymy się wzorcom Obserwatora i Mediatora.

    Szablon obserwatora

    Oto, co powiedziano o Observerze:

    We wzorcu obserwatora obiekt może posiadać listę obserwatorów, którzy są nim zainteresowani. koło życia. Za każdym razem, gdy obiekt zrobi coś interesującego, wysyła powiadomienie do swoich obserwatorów. Jeśli obserwator nie jest już zainteresowany słuchaniem obiektu, może go usunąć ze swojej listy.

    Potrzebujemy trzech metod, aby opisać ten wzór:

    publikuj (dane): Wywoływane przez obiekt, gdy ma powiadomienie. Część danych można przesłać tą metodą.
    subskrybuj(obserwator): Wywoływany przez obiekt w celu dodania obserwatora do swojej listy obserwatorów.
    unsubscribe(observer): Wywoływane przez obiekt w celu usunięcia obserwatora z listy obserwatorów.
    Większość nowoczesnych bibliotek JavaScript obsługuje te trzy metody w ramach infrastruktury zdarzeń. Zazwyczaj istnieje metoda on() lub connect(), metoda wyzwalacz() lub fire() oraz metoda off() lub detach(). Rozważ następujący fragment:

    //Po prostu tworzymy połączenie pomiędzy metodami zdarzeń jQuery var o = $(()); $.subscribe = o.on.bind(o); $.unsubscribe = o.off.bind(o); $.publish = o.trigger.bind(o); // Użycie document.on("tweetsReceived",function(tweets) ( //wykonaj pewne akcje, a następnie wywołaj zdarzenie $.publish("tweetsShow", tweets); )); //Możemy zasubskrybować to wydarzenie, a następnie uruchomić własne wydarzenie. $.subscribe("tweetsShow", funkcja() ( //wyświetl w jakiś sposób tweety .. //opublikuj po ich wyświetleniu $.publish("tweetsDisplayed); )); $.subscribe("tweetsDisplayed,function() (. ..));

    Wzorzec obserwatora jest jednym z najprostszych do wdrożenia wzorców i bardzo potężnym. JavaScript dobrze nadaje się do przyjęcia tego wzorca, ponieważ sam w sobie jest oparty na zdarzeniach. Następnym razem, gdy będziesz tworzyć aplikacje internetowe, rozważ opracowanie luźno powiązanych modułów i przyjęcie wzorca Observer jako środka komunikacji. Wzorzec obserwatora może stać się problematyczny, jeśli zaangażowanych jest zbyt wiele obiektów i obserwatorów.

    Mediator szablonów

    Ostatnim wzorcem, któremu się przyjrzymy, jest Mediator. Jest podobny do wzorca Obserwatora, ale z pewnymi zauważalnymi różnicami.

    Pośrednik ułatwia korzystanie z jednego wspólnego obiektu, który nawiązuje połączenie z kilkoma obiektami. Wszystkie obiekty oddziałują ze sobą poprzez pośrednika.

    W świecie tworzenia oprogramowania wzorzec mediatora jest używany, gdy system staje się zbyt złożony. Dzięki umieszczeniu pośredników komunikacja może odbywać się za pośrednictwem jednego podmiotu, zamiast komunikować się ze sobą wielu podmiotów. W tym sensie mediator może zastąpić system implementujący wzorce obserwatora.
    Porozmawiajmy o tym, jak możesz z niego skorzystać. Wyobraź sobie, że masz aplikację internetową, która pozwala użytkownikom kliknąć album i odtwarzać z niego muzykę. Możesz utworzyć mediatora w następujący sposób:

    $("#album").on("kliknij", funkcja(e) ( e.preventDefault(); var albumId = $(this).id(); mediator.publish("playAlbum", albumId); )) ; var playAlbum = funkcja(id) ( … mediator.publish("albumStartedPlaying", (lista utworów: [..], bieżący utwór: "Without You")); ); var logAlbumPlayed =function(id) ( //Zaloguj się do albumu na Benendzie); var updateUserInterface =function(album) ( //Aktualizacja interfejsu użytkownika, aby wyświetlał odtwarzaną zawartość); //Subskrypcje mediatora mediator.subscribe("playAlbum", playAlbum); mediator.subscribe("playAlbum", logAlbumPlayed); mediator.subscribe("albumStartedPlaying", updateUserInterface);

    Zaletą tego wzorca jest to, że za komunikację odpowiada jeden obiekt, natomiast we wzorcu obserwatora wiele obiektów może się wzajemnie słuchać i subskrybować.
    We wzorcu obserwatora nie ma ani jednego obiektu, który zawierałby ograniczenia. Zamiast tego obserwator i podmiot muszą współpracować, aby utrzymać ograniczenie. Wzorce komunikacji są zdeterminowane sposobami, w jakie obserwatorzy i podmioty są ze sobą powiązane: jeden podmiot ma zwykle wielu obserwatorów, a czasami obserwator jednego podmiotu jest podmiotem innego obserwatora.

    Wniosek

    Wspaniałą rzeczą we wzorcach projektowych jest to, że ktoś już z nich z powodzeniem korzystał w przeszłości. Istnieje wiele kodu open source, który implementuje różne wzorce w JavaScript. Jako programiści musimy zdawać sobie sprawę, jakie wzorce istnieją i kiedy je zastosować.

    Tłumaczenie ()
    Źródło zdjęć - Fotolia.ru

    Dzisiaj zagłębimy się w informatykę, poznając pewne typowe wzorce projektowe. Wzorce projektowe oferują programistom sposoby rozwiązywania problemów technicznych w elegancki sposób, który można ponownie wykorzystać. Chcesz zostać lepszym programistą JavaScript? Następnie czytaj dalej.

    Ponownie opublikowany podręcznik

    Co kilka tygodni ponownie przeglądamy niektóre z ulubionych artykułów naszych czytelników z całej historii witryny. Ten poradnik został po raz pierwszy opublikowany w lipcu 2012 roku.

    Wstęp

    Proste wzorce projektowe stanowią podstawowy element składowy obsługiwanych aplikacji. Jeśli kiedykolwiek brałeś udział w rozmowie technicznej, prawdopodobnie zostałeś o to zapytany. W tym samouczku przyjrzymy się kilku szablonom, z których możesz zacząć korzystać już dziś.

    Co to jest wzorzec projektowy?

    Wzorzec projektowy jest rozwiązaniem programowym wielokrotnego użytku

    Mówiąc najprościej, wzorzec projektowy to rozwiązanie programowe wielokrotnego użytku dla określonego rodzaju problemu, który często pojawia się podczas tworzenia oprogramowania. Przez lata praktyki tworzenia oprogramowania eksperci wymyślili sposoby rozwiązania takich problemów. Decyzje te zostały ujęte we wzorce projektowe. Więc:

    • wzorce są sprawdzonymi rozwiązaniami problemów związanych z tworzeniem oprogramowania
    • szablony są skalowalne, ponieważ zazwyczaj mają strukturę i zasady, których należy przestrzegać
    • szablony można ponownie wykorzystać w przypadku podobnych problemów

    W samouczku przyjrzymy się kilku przykładom wzorców projektowych.

    Rodzaje wzorców projektowych

    Podczas tworzenia oprogramowania wzorce projektowe dzieli się zwykle na kilka kategorii. Przyjrzymy się trzem najważniejszym z tych lekcji. Wyjaśniono je poniżej:

  • Wzorce generatywne koncentrują się na sposobach tworzenia obiektów lub klas. Może się to wydawać proste (i w niektórych przypadkach takie jest), ale duże aplikacje muszą kontrolować proces tworzenia obiektów.
  • Wzorce projektowania strukturalnego koncentrują się na sposobach zarządzania relacjami między obiektami, tak aby aplikacja była projektowana w sposób skalowalny. Kluczowym aspektem wzorców strukturalnych jest zapewnienie, że zmiana jednej części aplikacji nie wpłynie na wszystkie inne części.
  • Wzorce behawioralne koncentrują się na połączeniach między obiektami.
  • Po przeczytaniu tych krótkich opisów możesz mieć pytania. Jest to naturalne i wszystko stanie się jaśniejsze, gdy przyjrzymy się bardziej szczegółowo niektórym wzorcom projektowym. Więc czytaj dalej!

    Uwaga na temat klas w JavaScript

    Kiedy czytasz o wzorcach projektowych, często widzisz odniesienia do klas i obiektów. Może to być mylące, ponieważ JavaScript tak naprawdę nie ma konstrukcji klasy; bardziej poprawnym terminem jest „typ danych”.

    Typy danych JavaScript

    JavaScript to język zorientowany obiektowo, w którym obiekty są dziedziczone z innych obiektów w ramach koncepcji znanej jako dziedziczenie prototypowe. Typ danych można utworzyć, definiując funkcję zwaną konstruktorem, na przykład:

    Funkcja Osoba(config) ( this.name = nazwa.konfiguracji; this.age = config.age; ) Person.prototype.getAge = funkcja() ( return this.age; ); var tilo = nowa osoba((imię: „Tilo”, wiek: 23 )); konsola.log(tilo.getAge());

    Zwróć uwagę na użycie prototype przy definiowaniu metod dla typu danych Osoba. Ponieważ wiele obiektów Person odwołuje się do tego samego prototypu, pozwala to na użycie metody getAge() we wszystkich instancjach typu danych Person zamiast konieczności zastępowania jej w przypadku każdej instancji. Dodatkowo każdy typ danych dziedziczący po Person będzie miał dostęp do metody getAge().

    Praca z prywatnością

    Innym częstym problemem w JavaScript jest brak prawdziwego znaczenia zmiennych prywatnych. Możemy jednak użyć zamknięcia, aby trochę zasymulować prywatność. Rozważ następujący fragment:

    Var retinaMacbook = (funkcja() ( //Zmienne prywatne var RAM, addRAM; RAM = 4; //Metoda prywatna addRAM = funkcja (dodatkowaRAM) ( RAM += dodatkowaRAM; ); return ( //Zmienne i metody publiczne USB: niezdefiniowane , wstawUSB: funkcja (urządzenie) ( this.USB = urządzenie; ), usuńUSB: funkcja () ( var urządzenie = this.USB; this.USB = niezdefiniowane; zwróć urządzenie; ) ); ))();

    W powyższym przykładzie utworzyliśmy obiekt retinaMacbook ze zmiennymi i metodami publicznymi i prywatnymi. Oto jak go użyjemy:

    RetinaMacbook.insertUSB("myUSB"); konsola.log(retinaMacbook.USB); //wylogowuje "myUSB" console.log(retinaMacbook.RAM) //wylogowuje się niezdefiniowany

    Jest o wiele więcej rzeczy, które możemy zrobić z funkcjami i domknięciami w JavaScript, ale nie zajmiemy się tym wszystkim w tym samouczku. Mając za sobą tę małą lekcję na temat typów danych JavaScript i prywatności, możemy kontynuować badanie wzorców projektowych.

    Generatywne wzorce projektowe

    Istnieje wiele różnych typów szablonów tworzenia, ale w tym samouczku przyjrzymy się dwóm z nich: Builder i Prototype. Uważam, że są one używane na tyle często, że zasługują na uwagę.

    Szablon konstruktora

    Wzorzec Builder jest często używany w tworzeniu stron internetowych i prawdopodobnie używałeś go już wcześniej, nie zdając sobie z tego sprawy. Mówiąc najprościej, wzór ten można zdefiniować w następujący sposób:

    Użycie wzorca budowniczego pozwala nam na tworzenie obiektów poprzez określenie jedynie typu i zawartości obiektu. Nie musimy jawnie tworzyć obiektu.

    Na przykład prawdopodobnie robiłeś to niezliczoną ilość razy w jQuery:

    Var myDiv = $("To jest div."); //myDiv reprezentuje teraz obiekt jQuery odwołujący się do węzła DOM. var jakiś tekst = $("

    "); //someText jest obiektem jQuery odwołującym się do elementu HTMLParagraphElement var input = $("");

    Spójrz na trzy powyższe przykłady. W pierwszym przekazaliśmy element z pewną treścią. W drugim minęliśmy pusty tag

    W ostatnim minęliśmy element. Wynik wszystkich trzech był taki sam: zwrócono nam obiekt jQuery odwołujący się do węzła DOM.

    Zmienna $ implementuje wzorzec Builder w jQuery. W każdym przykładzie otrzymaliśmy obiekt JQuery DOM i mieliśmy dostęp do wszystkich metod udostępnianych przez bibliotekę jQuery, ale w żadnym przypadku nie wywołaliśmy jawnie document.createElement . Biblioteka JS poradziła sobie z tym wszystkim pod maską.

    Wyobraź sobie, ile pracy wymagałoby to, gdybyśmy musieli jawnie utworzyć element DOM i wstawić do niego treść! Korzystając ze wzorca budowniczego, możemy skupić się na typie i zawartości obiektu, a nie na jego jawnym tworzeniu.

    Szablon prototypu

    Wcześniej przyglądaliśmy się, jak definiować typy danych w JavaScript za pomocą funkcji i dodawać metody do obiektu prototypowego. Wzorzec Prototyp pozwala obiektom dziedziczyć od innych obiektów poprzez ich prototypy.

    Wzorzec prototypowy to wzorzec, w którym obiekty tworzone są na podstawie wzorca istniejącego obiektu poprzez klonowanie.

    Jest to prosty i naturalny sposób implementacji dziedziczenia w JavaScript. Na przykład:

    Var Osoba = (numFeet: 2, numHeads: 1, numHands:2); //Object.create pobiera swój pierwszy argument i stosuje go do prototypu nowego obiektu. var tilo = Obiekt.utwórz(Osoba); konsola.log(tilo.numHeads); //wypisuje 1 tilo.numHeads = 2; console.log(tilo.numHeads) //wyjścia 2

    Właściwości (i metody) obiektu Person są stosowane do prototypu obiektu tilo. Możemy zastąpić właściwości obiektu tilo, jeśli chcemy, aby były inne.

    W powyższym przykładzie użyliśmy Object.create() . Jednak przeglądarka Internet Explorer 8 nie obsługuje nowej metody. W takich przypadkach możemy symulować zachowanie:

    Var VehiclePrototype = ( init: funkcja (carModel) ( this.model = carModel; ), getModel: funkcja () ( console.log("Model tego pojazdu to " + this.model); ) ); funkcja pojazd (model) ( funkcja F() (); F.prototype = VehiclePrototype; var f = nowy F(); f.init(model); return f; ) var samochód = pojazd("Ford Escort"); samochód.getModel();

    Jedyną wadą tej metody jest to, że nie można określić właściwości tylko do odczytu, które można określić za pomocą Object.create() . Jednak prototypowy przykład pokazuje, jak obiekty mogą dziedziczyć od innych obiektów.

    Wzorce projektowania strukturalnego

    Wzorce projektowania strukturalnego są naprawdę przydatne przy definiowaniu sposobu działania systemu. Dzięki nim nasze aplikacje można łatwo skalować i zachować łatwość konserwacji. W tej grupie przyjrzymy się następującym wzorom: Kompozyt i Fasada.

    Szablon złożony

    Wzorzec złożony to kolejny wzorzec, którego prawdopodobnie używałeś wcześniej, bez żadnej implementacji.

    Wzorzec złożony stwierdza, że ​​grupę obiektów można traktować w taki sam sposób, jak pojedynczy obiekt w grupie.

    Co to oznacza? Cóż, rozważ ten przykład w jQuery (większość bibliotek JS będzie miała odpowiednik tego):

    $(.myList").addClass("wybrane"); $("#myItem").addClass("wybrane"); //nie rób tego na dużych tabelach, to tylko przykład. $("#dataTable tbody tr").on("kliknij", funkcja(event)( alert($(this).text()); ) ); $("#myButton").on("kliknięcie", funkcja(zdarzenie) ( alert("Kliknięty."); ));

    Większość bibliotek JavaScript zapewnia spójne API niezależnie od tego, czy mamy do czynienia z pojedynczym elementem DOM, czy tablicą elementów DOM. W pierwszym przykładzie możemy dodać klasę Select do wszystkich elementów wybranych selektorem .myList, ale tę samą metodę możemy zastosować pracując z pojedynczym elementem DOM, #myItem . Podobnie możemy dołączyć procedury obsługi zdarzeń za pomocą metody on() na wielu węzłach lub na pojedynczym węźle za pośrednictwem tego samego API.

    Korzystając ze wzorca Composite, jQuery (i wiele innych bibliotek) udostępnia nam uproszczone API.

    Szablon złożony może czasami powodować problemy. W języku o luźnej typografii, takim jak JavaScript, często warto wiedzieć, czy mamy do czynienia z pojedynczym elementem, czy z wieloma elementami. Ponieważ wzór złożony wykorzystuje w obu przypadkach ten sam interfejs API, czasami możemy pomylić jeden obiekt z kilkoma i odwrotnie. Niektóre biblioteki, takie jak YUI3, oferują dwie oddzielne metody pobierania elementów (Y.one() vs Y.all()).

    Fasada

    Oto kolejny duży obraz, który uważamy za oczywisty. Jest to właściwie jeden z moich ulubionych, ponieważ jest prosty i widziałem, że jest używany wszędzie, aby pomóc nam w przypadku niespójności przeglądarki. Oto czym jest szablon Fasady:

    Szablon Fasada zapewnia użytkownikowi prosty interfejs, jednocześnie ukrywając jego złożoność.

    Wzorzec Fasada prawie zawsze poprawia użyteczność oprogramowania. Używając ponownie przykładu jQuery, jedną z najpopularniejszych metod biblioteki jest metoda Ready() :

    $(document).ready(function() ( //tutaj trafia cały kod... ));

    Metoda Ready() faktycznie implementuje fasadę. Jeśli spojrzysz na kod źródłowy, znajdziesz to:

    Gotowy: (function() ( ... //Mozilla, Opera i Webkit if (document.addEventListener) ( document.addEventListener("DOMContentLoaded", idempotent_fn, false); ... ) //Model zdarzeń IE else if ( document.attachEvent) ( // upewnij się, że zostanie uruchomione przed załadowaniem; może być późno, ale bezpieczne także dla ramek iframe document.attachEvent("onreadystatechange", idempotent_fn); // Powrót do window.onload, który zawsze będzie działać window.attachEvent("onload" , idempotent_fn); ... ) ))

    W rzeczywistości metoda Ready() nie jest taka prosta. jQuery normalizuje niespójności przeglądarki, aby mieć pewność, że funkcja Read() zostanie uruchomiona w odpowiednim momencie. Jednak jako programista masz do dyspozycji prosty interfejs.

    Większość przykładów wzoru fasady jest zgodna z tą zasadą. Implementując je, zwykle opieramy się na ukrytych instrukcjach warunkowych, ale przedstawiamy je jako prosty interfejs dla użytkownika. Inne metody implementujące ten wzorzec to animate() i css(). Czy zastanawiasz się, dlaczego wykorzystali wzór fasady?

    Behawioralne wzorce projektowe

    Wszelkie systemy oprogramowania zorientowanego obiektowo będą miały relacje między obiektami. Brak zorganizowania tej komunikacji może prowadzić do błędów trudnych do znalezienia i skorygowania. Wzorce projektowania zachowań dyktują różne sposoby organizowanie komunikacji pomiędzy obiektami. W tej sekcji przyjrzymy się wzorcom Obserwatora i Mediatora.

    Wzór obserwatora

    Wzorzec Obserwatora jest pierwszym z dwóch zachowań, przez które będziemy przechodzić. Oto jego definicja:

    W szablonie obserwatora jednostka może mieć listę obserwatorów zainteresowanych jej cyklem życia. Za każdym razem, gdy obiekt zrobi coś interesującego, wysyła powiadomienie do swoich obserwatorów. Jeśli obserwator nie jest już zainteresowany słuchaniem podmiotu, podmiot może go usunąć ze swojej listy.

    Brzmi całkiem prosto, prawda? Potrzebujemy trzech metod, aby opisać ten wzór:

    • publikuj(dane): Wywoływane przez podmiot, gdy otrzyma powiadomienie. Do tej metody można przekazać część danych.
    • subskrybuj(obserwator): wywoływane przez podmiot w celu dodania obserwatora do swojej listy obserwatorów.
    • unsubscribe(observer): Wywoływane przez podmiot w celu usunięcia obserwatora z listy obserwatorów.

    Okazuje się, że większość nowoczesnych bibliotek JavaScript obsługuje te trzy metody w ramach struktury zdarzeń użytkownika. Zazwyczaj istnieje metoda on() lub connect(), metoda wyzwalacz() lub fire() oraz metoda off() lub detach(). Rozważ następujący fragment:

    //Po prostu tworzymy powiązanie pomiędzy metodami zdarzeń jQuery //i metodami zalecanymi przez wzorzec Observer, ale nie musisz. var o = $(()); $.subscribe = o.on.bind(o) ; $.unsubscribe = o.off.bind(o); $.publish = o.trigger.bind(o); // Użycie document.on("tweetsReceived", funkcja(tweets) ( //wykonaj pewne akcje, a następnie uruchom wydarzenie $.publish("tweetsShow", tweets); )); //Możemy zasubskrybować to wydarzenie, a następnie uruchomić własne wydarzenie. $.subscribe("tweetsShow", funkcja() ( //w jakiś sposób wyświetl tweety .. //opublikuj akcję po jej wyświetleniu. $.publish("tweetsDisplayed); )); $.subscribe("tweetyWyświetlone, funkcja() ( ... ));

    Szablon Observer jest jednym z najbardziej popularnych proste modele do wdrożenia, ale jest bardzo potężny. JavaScript dobrze nadaje się do przyjęcia tego wzorca, ponieważ jest w naturalny sposób oparty na zdarzeniach. Następnym razem, gdy będziesz tworzyć aplikacje internetowe, rozważ stworzenie modułów, które są ze sobą luźno powiązane i użyj wzorca Observer jako środka komunikacji. Wzorzec obserwatora może stać się problematyczny, jeśli jest zbyt wielu podmiotów i obserwatorów. Może się to zdarzyć w systemach o dużej skali, a następny wzorzec, któremu się przyjrzymy, próbuje rozwiązać ten problem.

    Mediator

    Ostatnim wzorcem, któremu się przyjrzymy, jest wzór mediatora. Jest to podobne do wzorca Obserwatora, ale z pewnymi zauważalnymi różnicami.

    Wzorzec mediatora zachęca do używania jednego wspólnego obiektu, który obsługuje komunikację z wieloma obiektami. Wszystkie obiekty komunikują się ze sobą poprzez pośrednika.

    Dobrą analogią ze świata rzeczywistego byłaby wieża lotniska, która obsługuje komunikację między lotniskiem a lotami. W świecie tworzenia oprogramowania wzorzec Mediator jest często używany, gdy system staje się zbyt złożony. Dzięki umieszczeniu mediatorów komunikacja może odbywać się za pośrednictwem pojedynczego obiektu, a nie wielu obiektów komunikujących się ze sobą. W tym sensie wzorzec mediatora można wykorzystać do zastąpienia systemu implementującego wzorzec obserwatora.

    Oto uproszczona implementacja wzorca pośrednika Addy Osmani. Porozmawiajmy o tym, jak możesz z niego skorzystać. Wyobraź sobie, że masz aplikację internetową, która pozwala użytkownikom kliknąć album i odtwarzać z niego muzykę. Mediator można skonfigurować w następujący sposób:

    $("#album").on("kliknij", funkcja(e) ( e.preventDefault(); var albumId = $(this).id(); mediator.publish("playAlbum", albumId); )) ; var playAlbum = funkcja(id) ( … mediator.publish("albumStartedPlaying", (lista utworów: [..], bieżący utwór: "Without You")); ); var logAlbumPlayed =function(id) ( //Zaloguj album w backendzie ); var updateUserInterface =function(album) ( //Aktualizuj interfejs użytkownika, aby odzwierciedlał to, co jest odtwarzane); //Subskrypcje mediatora mediator.subscribe("playAlbum", playAlbum); mediator.subscribe("playAlbum", logAlbumPlayed); mediator.subscribe („albumStartedPlaying”, updateUserInterface);

    Przewagą tego wzorca nad wzorcem Obserwator jest to, że za komunikację odpowiada pojedynczy obiekt, podczas gdy we wzorcu Obserwator wiele obiektów może się wzajemnie nasłuchiwać i subskrybować.

    Wzorzec Obserwatora nie zawiera pojedynczego obiektu hermetyzującego ograniczenie. Zamiast tego obserwator i podmiot muszą współpracować, aby utrzymać ograniczenie. Wzorce komunikacji zależą od wzajemnych powiązań obserwatorów i podmiotów: jeden podmiot ma zwykle wielu obserwatorów, a czasami obserwator jednego podmiotu jest podmiotem innego obserwatora.

    Wniosek

    Ktoś już to z powodzeniem stosował w przeszłości.

    Wspaniałą rzeczą we wzorcach projektowych jest to, że ktoś już z nich z powodzeniem korzystał w przeszłości. Istnieje wiele kodów open source, które implementują różne wzorce w JavaScript. Jako programiści musimy wiedzieć, jakie wzorce są dostępne i kiedy z nich korzystać. Mam nadzieję, że ten samouczek pomógł Ci zbliżyć się o krok do odpowiedzi na te pytania.

    Do dalszej lektury

    Duża część treści tego artykułu znajduje się w doskonałej książce „” autorstwa Addy Osmani. Jest to książka internetowa, która została udostępniona bezpłatnie na licencji Creative Commons. Książka szerzej omawia teorię i implementację wielu różnych wzorców, zarówno w waniliowym JavaScript, jak i w różnych bibliotekach JS. Zachęcam do zapoznania się z nim przy rozpoczęciu kolejnego projektu.

    Być może znasz już frameworki/biblioteki takie jak ReactJS, Angular czy jQuery, ale czy wiesz, dlaczego zostały stworzone? Jaki problem rozwiązują? Jaki wzorzec projektowy jest używany? Wzorce projektowe są bardzo ważne i mogę sobie wyobrazić, że niektórzy programiści-samouki mogli je przeoczyć. Czym zatem są wzorce projektowe? Co oni robią? Jak możemy z nich skorzystać? Dlaczego warto je stosować?

    Czym są wzorce projektowe i po co z nich korzystać? W tworzeniu oprogramowania wzorzec projektowy jest podstawowym rozwiązaniem wielokrotnego użytku typowego problemu dany kontekst. Nie jest to gotowy układ, który można bezpośrednio przekształcić w kod źródłowy lub maszynowy. Jest to opis lub szablon rozwiązania problemu, który może pojawić się w różnych sytuacjach. Wzorce projektowe to ustalone najlepsze praktyki, które programista może wykorzystać do rozwiązania typowych problemów podczas tworzenia aplikacji lub systemu.

    Wzorce projektowe to sprawdzony sposób na rozwiązywanie problemów. Nie obejmują oczywistych rzeczy, takich jak używanie dla pętli do iteracji po elementach tablicy. Służą do rozwiązywania bardziej złożonych problemów, które napotykamy podczas tworzenia dużych aplikacji.

    Oto niektóre korzyści wynikające ze stosowania wzorców projektowych:

    • Nie trzeba wymyślać koła na nowo (leniwy programista => dobry programista)
    • Znajdź wspólny język z programistami
    • Wyglądasz fajnie i profesjonalnie
    • Jeśli jesteś samoukiem, pomoże Ci to wyróżnić się na tle konkurencji.
    • Wzorce te działają we wszystkich językach programowania
    Rodzaje szablonów i przykłady niektórych z nich

    Wzorce twórcze: tworzenie nowych obiektów.

  • Konstruktor
  • Modułowy
  • Metoda fabryczna
  • Pojedyncza
  • Wzory strukturalne: organizuj obiekty.

  • Dekorator
  • Fasada
  • Behawioralne(Behawioralne): jak obiekty odnoszą się do siebie.

  • Obserwator
  • Mediator
  • Komenda
  • Wzorce generatywne

    Szablony te służą do tworzenia nowych obiektów.

    Konstruktor

    Tworzy nowe obiekty w swoim własnym zakresie.

    // Wzorzec konstruktora //
    // Służy do tworzenia nowych obiektów w ich własnym zakresie. var Osoba = funkcja (imię, wiek, ulubione jedzenie) (
    this.name = imię;
    this.age = wiek;
    this.favFood = ulubioneJedzenie;
    ); // Prototyp pozwala wszystkim instancjom Person na odwoływanie się do niego bez powtarzania funkcji.
    Osoba.prototyp.greet = funkcja() (
    console.log(`Witam, nazywam się $(this.name), mam $(this.age) lat, a moim ulubionym jedzeniem jest $(this.favFood)`); // new tworzy obiekt ( ) i przekazuje „to” konstruktorowi
    // Konstruktor ustawia wartość dla tego obiektu i zwraca ją.
    var bob = nowa osoba("Bob", 22, "Kurczak");
    bob.pozdrawiam();
    // Cześć, mam na imię Bob, mam 22 lata i moją ulubioną potrawą jest Kurczak // Zajęcia ES6 / ES2015
    Pojazd klasy (
    konstruktor(typ, kolor) (
    this.type = typ;
    this.color = kolor;
    ) getSpecs() (
    console.log(`Typ: $(ten.typ), Kolor: $(ten.kolor)`);
    }
    ); var SomeTruck = nowy pojazd("Ciężarówka", "czerwony");
    SomeTruck.getSpecs();

    Moduł

    Służy do hermetyzacji metod.

    // Wzorzec modułu //
    // Służy do enkapsulacji kodu var myModule = (function() (
    // Zmienna prywatna
    var memes = ["koty", "dog", "harambe"];

    Var getMemes = funkcja() (
    zwróć memy;
    ); // zwraca to, do czego chcesz zezwolić na dostęp w obiekcie
    // sposób, w jaki to zwraca, naprawdę sprawia, że ​​jest to wskaźnik powrotu wzorca projektowania modułowego (
    getMemes: getMemes
    };
    ))(); konsola.log(myModule.getMemes()); // tablica memów
    konsola.log(myModule.memes); //nieokreślony

    Fabryka

    Służy do uproszczenia tworzenia obiektów, ułatwia generowanie instancji obiektów, nie wymaga użycia konstruktora.

    // Wzór fabryczny //
    // Kilku konstruktorów naszej funkcji fabrycznej Cat(options) (
    this.sound = "Miau";
    ta.nazwa = nazwa.opcji;
    ) funkcja Pies(opcje) (
    this.sound = "Rawr";
    ta.nazwa = nazwa.opcji;
    ) //Fabryka zwierząt
    funkcja AnimalFactory() () // Typ domyślny Kat
    AnimalFactory.prototype.animalType = Kot; // metoda tworzenia nowych zwierząt
    AnimalFactory.prototype.createAnimal = funkcja(opcje) (
    switch(opcje.typ zwierzęcia) (
    przypadek „kot”:
    this.animalType = Kot;
    przerwa;
    przypadek „pies”:
    this.animalType = Pies;
    przerwa;
    domyślny:
    this.animalType = Kot;
    przerwa;
    ) zwróć nowy this.animalType(opcje);
    ) var fabryka zwierząt = nowa fabryka zwierząt();
    var doge = fabryka zwierząt.createAnimal((
    typ zwierzęcia: „pies”,
    imię: „Doga”
    )); var snowball = AnimalFactory.createAnimal((nazwa: „Snowball”)); console.log(instancja psa); // PRAWDA
    konsola.log(doge); // wyświetla doge jako obiekt kota
    console.log(instancja Cat w kształcie kuli śnieżnej); // PRAWDA
    konsola.log(śnieżka); // wyświetla śnieżkę jako obiekt kota

    Singel

    Użyj, aby ograniczyć się do jednej instancji obiektu.

    Chociaż Singleton ma swoje zastosowania, zwykle jeśli zauważymy, że potrzebujemy go w JavaScript, jest to znak, że powinniśmy ponownie ocenić nasz projekt. Zwykle oznacza to, że moduły w systemie są albo ściśle powiązane, albo że ta logika jest nadmiernie rozproszona we wszystkich częściach kodu. Singleton jest trudniejszy do przetestowania ze względu na problemy związane z ukrytymi zależnościami, trudnościami w tworzeniu wielu instancji, trudnościami w instalowaniu zależności itp. Wzory strukturalne

    Jak powstają obiekty i jakie są relacje pomiędzy nimi. Rozszerza lub upraszcza funkcjonalność.

    Dekorator

    Służy do dodawania nowych funkcji do obiektów ( Rozszerza funkcjonalność).

    // Wzór dekoratora //
    // Prosty konstruktor var Osoba = funkcja (nazwa) (
    this.name = imię;
    ) Osoba.prototype.greet = funkcja () (
    console.log(`Witam, nazywam się $(this.name)`);
    ) var unikalnyBob = nowa osoba("Bob"); // można do niego dodać bez zmiany konstruktora Person
    unikatowyBob.hobbies = ["Gotowanie", "Bieganie"]; unikalnyBob.greet = funkcja() (
    console.log("Moje hobby to: ", to.hobby);
    ); unikalnyBob.greet(); // Inny sposób
    var CoolPerson = funkcja(nazwa, catchPhrase) (
    Osoba.call(to, imię);
    this.catchPhrase = catchPhrase;
    ); // zawiera prototypy z Person
    CoolPerson.prototype = Obiekt.utwórz(Osoba.prototyp); // zmienia prototyp
    CoolPerson.prototype.greet = funkcja() (
    Person.prototype.greet.call(this);
    konsola.log(this.catchPhrase);
    ); var coolDude = new CoolPerson("Jeff", "Aaaayyy");
    konsola.log(coolDude);
    fajnyKoleś.pozdrawiam();

    Fasada

    Użyj, aby utworzyć prosty interfejs ( upraszcza funkcjonalność, takie jak jQuery).

    // Wzór fasady //
    // usuwa niektóre skomplikowane/niechlujne rzeczy var $ = funkcja (cel) (
    zwróć nowe MemeQuery (cel);
    ); funkcja MemeQuery (cel) (
    this.target = document.querySelector(target);
    ) MemeQuery.prototype.html = funkcja(html) (
    this.target.innerHTML = html;
    zwróć to;
    ); // teraz jedyne co zobaczymy i wykorzystamy to $
    $("#myParagraph").html("Meeemee").html("Niektóre wzorce projektowe JS"); //ok, może to nie jest najlepszy przykład..
    // spójrz tylko na kod źródłowy jQuery, jest tam mnóstwo przykładów fasadowych
    // jest to potrzebne po prostu, aby oderwać nas od skupiania się na złożoności projektowania i sprawić, że projektowanie będzie szybsze i łatwiejsze.

    Wzorce zachowań

    Przypisz obowiązki między obiektami i sposób, w jaki się komunikują.

    Obserwator

    Umożliwia obiektom obserwację obiektów i otrzymywanie powiadomień o zmianach.

    //Wzorzec obserwatora //
    // https://github.com/CodeDraken/emtr/blob/master/emitter.js

    Mediator

    Jeden obiekt steruje komunikacją między obiektami, więc obiekty nie komunikują się ze sobą bezpośrednio.

    // Wzorzec mediatora //
    // Przepraszam, jestem zbyt zmęczony, żeby wymyślać przykłady :P

    Komenda

    Hermetyzuje wywołanie metody w pojedynczym obiekcie.

    //Wzorzec poleceń //
    // Przykład z:
    (funkcjonować())(
    var carManager = ( // Żądanie informacji
    requestInfo: funkcja (model, identyfikator) (
    return "Informacje dla " + model + " z identyfikatorem " + id + " to foobar";
    },
    // Kupić samochód
    kuppojazd: funkcja(model, identyfikator)(
    return „Pomyślnie kupiłeś przedmiot „ + id + ”, a „ + model;
    ), // Organizuj przeglądanie
    aranżacja oglądania: funkcja (model, identyfikator) (
    return "Zarezerwowałeś obejrzenie " + model + " (" + id + ") ";
    }
    };
    ))(); carManager.execute = funkcja (nazwa) (
    return carManager && carManager.apply(carManager, .slice.call(arguments, 1));
    ); carManager.execute("arrangeViewing", "Ferrari", "14523");
    carManager.execute("Informacje o żądaniu", "Ford Mondeo", "54323");

    Uff, tyle tych szablonów... Mam nadzieję, że nadal tu jesteś, ponieważ mamy jeszcze 22 szablony do przejrzenia. Żartuję, to wszystko! Ale nadal istnieją wzorce do zbadania, więc dodam linki dla tych, którzy chcą dowiedzieć się więcej na ten temat.

    Niektóre wzorce projektowe w JavaScript

    P.S. To moje pierwsze tłumaczenie dla medium. Dziękuję wszystkim za przeczytanie! Mam nadzieję, że faktycznie nauczyłeś się czegoś nowego. Bardzo chciałbym usłyszeć Twoje komentarze!

    W ciągu ostatnich kilku miesięcy w Internecie toczyła się gorąca debata na temat najlepszego sposobu radzenia sobie z wydarzeniami. Google najpierw udostępniło bibliotekę JsAction, a następnie ogłosiło metodę Object.observe(), która będzie zaimplementowana w standardzie ECMAScript 7, ale jest już obsługiwana w Chrome 36 i Node.js Harmony.

    Programiści są podzieleni co do tego, czy konieczne jest przechowywanie całej logiki w plikach JS, czy dopuszczalne jest wstawianie poszczególnych części logiki do HTML. W tym artykule postaramy się nadać sens tej debacie, przyglądając się różnym wzorcom obsługi błędów, a następnie rozważając zalety i wady wszystkich alternatyw.

    Dane

    JsAction to biblioteka Google do delegowania zdarzeń w JS. Opiera się na bibliotece Closure i po raz pierwszy została użyta w Mapach Google kilka lat temu do rozwiązywania problemów z obsługą zdarzeń w niektórych przeglądarkach. JsAction ma na celu oddzielenie zdarzeń od metod ich obsługi i w tym celu przenosi część logiki obsługi zdarzeń do HTML.

    Ogólnie rzecz biorąc, jest to jeden z trendów w tworzeniu stron internetowych - przenoszenie części logiki nie tylko do HTML, ale do wewnątrz elementów DOM, na które ta logika wpływa. I dotyczy to nie tylko przetwarzania zdarzeń, istnieją frameworki oparte na szablonach (Angular, Ractive, React), które implementują wzorzec Model-View-Controller w aplikacjach internetowych, umożliwiając wiązanie danych i programowanie reaktywne.

    Wprowadzenie metody Object.observe() w kolejnej specyfikacji ECMAScript to kolejny krok w tym kierunku, gdyż daje programistom natywną implementację wzorca Publisher/Subscriber, który ma zastosowanie w wielu sytuacjach, nie tylko przy obsłudze zdarzeń. Frameworki deklaratywne już bazują na tym podejściu, a implementacja Object.observe() pozwoli im osiągnąć większą wydajność.

    Wycieczka historyczna

    Od początków JavaScriptu podejście do obsługi zdarzeń zmieniało się kilkukrotnie. Początkowo istniał tylko jeden sposób dodania elementów dynamicznych do strony – dodanie atrybutu do żądanego tagu i połączenie z nim fragmentu kodu JavaScript. Możesz napisać kod bezpośrednio w atrybucie lub wywołać jedną z funkcji zdefiniowanych w przestrzeni globalnej.

    Oto jak w tamtych czasach wyglądała zmiana koloru tła strony za pomocą kliknięcia myszką:

    Być przygnębionym

    Nie trwało długo, zanim ujawniono ograniczenia i niebezpieczeństwa związane z używaniem JS w atrybutach tagów HTML. W listopadzie 2000 r. dołączono standard ECMAScript 3 nowy sposób wiązanie procedur obsługi ze zdarzeniami przeglądarki. W tym momencie firma Microsoft zaimplementowała już metodę załącznikEvent() w swojej przeglądarce, ale minęło trochę czasu, zanim metody te zaczęły być stosowane. Zaledwie 4 lata po pierwszej wzmiance o niej (przez twórców Netscape Navigator) koncepcja dyskretnego JS zaczęła się rozprzestrzeniać.

    Twórcy Netscape Navigator wierzyli, że nasłuchiwanie zdarzeń rozwiązuje następujące niedociągnięcia wbudowanych procedur obsługi zdarzeń:

    • Mieszanie kodu i znaczników sprawia, że ​​kod jest mniej czytelny i trudniejszy w utrzymaniu.
    • Zanieczyszczenie globalnej przestrzeni nazw: cały kod wbudowany jest zdefiniowany w przestrzeni globalnej, dlatego każda funkcja wywołująca te procedury obsługi musi być również globalna.
    • Jest to cel ataków XSS; atrybut może zawierać dowolny kod, w tym złośliwy kod, który można przekazać do eval bez jakiejkolwiek weryfikacji.

    W 2006 roku wraz z wydaniem pierwszych bibliotek skupiono się na szerokie zastosowanie Ajax, taki jak YUI i Jquery, zastosowano podejście do nasłuchiwania zdarzeń bez czekania, co pozwoliło na szybsze przyjęcie najlepszych praktyk, które ostatecznie przyjęła większość programistów.

    Jednocześnie do metody nasłuchiwania zdarzeń dodano:

    • Skalowalność: hermetyzacja procedury obsługi zdarzeń w funkcji jest zgodna z zasadą DRY, ponieważ umożliwia pracę na poziomie prototypu obiektu i używanie tej samej logiki dla wielu procedur obsługi; i za pomocą mechanizmu selektora CSS JQuery daje to łatwe i skuteczna metoda wiązanie procedur obsługi z zestawem węzłów:
    $(dokument).ready(function () ( $(.clickable").click(function () ( document.body.style.background="lightblue"; return false; )); ));
    • Debugowanie: dzięki narzędziom takim jak Firebug i Chrome Developer Tools debugowanie JS staje się mniejszym koszmarem w porównaniu z debugowaniem kodu wbudowanego.
    Problemy ze wzorcem addEventListener

    Podejście nasłuchujące, mimo oczywistych zalet, ma też wady:

    • Dołączanie słuchaczy do obiektów może prowadzić do wycieków pamięci w zamknięciach, jeśli zostanie wykonane nieprawidłowo. Zamknięcia są jedną z najpotężniejszych funkcji JS, ale należy ich używać ostrożnie, gdy są powiązane z elementami DOM. Zamknięcia mają wskaźnik do ich zakresu. W rezultacie dołączenie zamknięcia do elementu DOM może spowodować podróż do niego w obie strony, a w konsekwencji wyciek pamięci. Oto przykład z przewodnika po stylu JavaScript firmy Google z przykładami poprawnych i niepoprawnych implementacji.
    • IE też ma problem z zbieraniem śmieci, zwłaszcza jeśli chodzi o zdarzenia. Oprócz dobrze znanego problemu wzajemnych odwołań cyklicznych, starsze wersje IE nie usuwają procedury obsługi po usunięciu elementu DOM, co pociąga za sobą dodatkowe wycieki.
    Jeszcze raz: czym jest JsAction?

    Teraz dochodzimy do JsAction. Jak wspomniano na początku posta, jest to biblioteka delegowania zdarzeń, która pozwala powiązać zdarzenia i procedury obsługi poprzez ich nazwy, używając specjalnego Atrybut HTML jsaction, która będzie przetwarzana przez bibliotekę.

    Każdy moduł obsługi zdarzeń jest oddzielnie rejestrowany w jednym lub większej liczbie plików lub skryptów JS; są one powiązane z nazwami metod, a ponieważ związek między nazwami i funkcjami jest obsługiwany przez bibliotekę, nie ma potrzeby dodawania ich do kontekstu globalnego.

    Ogólnie rzecz biorąc, użycie JsAction powinno zapewnić następujące korzyści:

  • Rozwiązanie problemu wycieków pamięci w starszych przeglądarkach.
  • Zapobiegaj lub ograniczaj zanieczyszczenie globalnej przestrzeni nazw.
  • Zmniejszenie zależności przetwarzania zdarzeń od konkretnej implementacji przeglądarki.
  • Lepsza wydajność i skalowalność, umożliwiając zainstalowanie jednego detektora zdarzeń na stronie, a następnie kierowanie zdarzeń przez niego do ich procedur obsługi.
  • Próbkę JsAction można zobaczyć na stronie GitHub biblioteki.

    Szczerze mówiąc, kod w przykładzie nie jest zbyt łatwy do zrozumienia i nie tak prosty, jak można by się spodziewać. Większość wyników można osiągnąć za pomocą kilku linijek kodu. Globalne zanieczyszczenie przestrzeni nazw można ograniczyć za pomocą wzorców modułów lub przestrzeni nazw. Leniwe ładowanie można łatwo osiągnąć, instalując na początku kody pośredniczące zamiast procedur obsługi zdarzeń, a następnie asynchronicznie ładując zewnętrzny skrypt z prawdziwymi procedurami obsługi i ponownie przypisując do nich zdarzenia.

    Implementacja punktów 3 i 4 jest bardziej złożona: musimy ustawić pojedynczy moduł obsługi dla całej strony, ustawić atrybut na elementach DOM, określić, której metody będziemy używać jako obsługi i stworzyć „super procedurę obsługi”, która będzie kieruj zdarzenia do procedur obsługi.

    Jeszcze raz zaznaczę, że niekoniecznie jest to właściwe rozwiązanie dla Twoich konkretnych zadań, wszystko zależy od charakterystyki Twojego projektu. Pomimo wielu zalet ma pewne wady:

  • Biblioteka nie jest lekka.
  • Nie wydaje się szczególnie intuicyjny w użyciu, a krzywa uczenia się może być dość stroma dla początkujących. Dokumentacja na ten temat jest w tej chwili wyraźnie niewystarczająca.
  • Rozpoczęcie pracy z tym może być dość trudne. W przypadku braku gotowej wersji skompilowanej należy najpierw pobrać bibliotekę Closure za pomocą kompilatora i skompilować JsAction.
  • Ramy deklaratywne

    JsAction nie jest ostatecznym rozwiązaniem do obsługi zdarzeń w JS i jak widzieliśmy, zostało opracowane dość dawno temu, choć nie jako projekt open source. A po odkryciu jego kodu rozpoczęła się ożywiona debata między krytykami i entuzjastami. Jednym z powodów jest fakt, że frameworki deklaratywne, których popularność stale rośnie, wykorzystują to samo podejście, przy wysokim stopniu integracji logiki i prezentacji. W tych frameworkach kod wbudowany jest zwracany nie tylko w celu obsługi zdarzeń, ale nawet w celu wypełnienia elementów strony treścią.

    Czekaj, ale mieszanie logiki aplikacji i prezentacji jest złe? Tak. Przyjrzeliśmy się niektórym zaletom oddzielenia logiki od prezentacji, takim jak łatwość debugowania i przejrzystość kodu. Czasami jednak obsługę projektu można ulepszyć, określając logikę zastosowaną do obiektu bezpośrednio w samym obiekcie.

    Frameworki takie jak RactiveJs, Angular, Ember i React pozwalają nie tylko na osadzanie kodu w widokach. W szerokim zakresie korzystają z modeli opartych na szablonach, które umożliwiają powiązanie procedur obsługi zdarzeń, danych i logiki prezentacji w elementach DOM, a następnie specjalizację w różnych skryptach. W zasadzie ten sam schemat jest używany w JsAction do oddzielenia nazw procedur obsługi zdarzeń i ich implementacji. W rezultacie zwiększają separację między logiką a prezentacją, wypychając aplikacje Oparty na MVC na wyższy poziom i jednocześnie uprościć korzystanie z szablonów.

    Frameworki te umożliwiają znacznie więcej niż proste przetwarzanie zdarzeń. Zapewniają powiązanie danych, które jest niezbędne do pomyślnego wdrożenia oddzielenia MVC. Wiążąc elementy widoku z obiektami JS, widoki można natychmiast aktualizować w przypadku zmiany obiektów. Ponadto aktualizacja jest szczególnie wydajna, ponieważ zmieniana jest tylko minimalna liczba elementów DOM, co ogranicza ponowne renderowanie strony, które jest zazwyczaj głównym czynnikiem zmniejszającym prędkość stron internetowych.

    W tym samym celu Ractive i React wykorzystują wirtualny DOM – abstrakcyjną reprezentację DOM, która umożliwia bardzo szybkie operacje poprzez ograniczenie wymaganej manipulacji DOM. Są do siebie bardzo podobni, skupiając się na programowaniu reaktywnym i wizualizacji. W przeciwieństwie do Angulara, który nie skupia się na widokach, ale jest bardziej kompleksowym frameworkiem, pozwalającym na obsługę routingu, połączenia z serwerem itp.

    Wszystkie te frameworki obsługują wiązanie dwukierunkowe, wygodnym sposobem aby osiągnąć spójność pomiędzy stanem elementów DOM a logiką aplikacji. Na przykład, jeśli chcesz wyświetlić listę czegoś. W tradycyjnym paradygmacie imperatywnym potrzebny byłby kod podobny do tego:

    funkcja createItemHTML (val) ( return "" + val + ""; ) funkcja displayList (kontener, elementy) ( kontener.empty(); $.each(przedmioty, funkcja (indeks, wartość) ( var element = $("" ); element.attr("id", "div_" + indeks); element.html(createItemHTML(val)); pojemnik.append(element); )); ) funkcja editItem (kontener, itemId, itemValue) ( ​​​​var element = kontener.find("#" + itemId); if (element) ( element.html(createItemHTML(itemValue)); ) ) //... displayList($("#container"), elementy); //... editItem(kontener, identyfikator, nowaWartość);

    Ten kod jest dobry, pozwala uniknąć niepotrzebnych powtórzeń, ale nadal mieszamy logikę i prezentację, choć w inny sposób.

    A oto jak wygląda rozwiązanie tego samego problemu przy użyciu Ractive:

    (#items:num) (/items) var ractive = new Ractive(( el: "kontener", szablon: "#listTemplate", dane: ( "elementy": elementy ) )); ractive.on(( "itemClick": funkcja (e) ( //dostęp do e.node i e.context zarówno dla elementu DOM // jak i powiązanego z nim stanu Ractive ) )); //... //Teraz zaktualizuj elementy o nową listę ractive.set("items", newItemsList);

    Wszystko! Aby zaktualizować stronę, nie trzeba pisać kodu. Ractive zrobi to za Ciebie. Jest bardziej przejrzysty, łatwiejszy w utrzymaniu, lepiej zaprojektowany i wydajniejszy. Możemy także na bieżąco dodawać moduły obsługi zdarzeń.

    Obiekt.observe()

    Object.observe() to spojrzenie w przyszłość, bo nie ma go nawet w 6. specyfikacji EcmaSript, jest dopiero w najnowszej, 7. wersji. Jednak Google już prawie zaimplementował ją w najnowszej wersji swojej przeglądarki (Chrome 36), a biblioteka Polymer Observe-JS oferuje polifill dla tej metody w nowoczesnych przeglądarkach, dopóki nie pojawi się w nich natywna obsługa.

    Metoda ta umożliwia asynchroniczną obserwację zmian w obiektach i tablicach. Obserwatorzy otrzymają na bieżąco dane o zmianach, jakie zaszły w monitorowanych obiektach. Dzięki Object.observe() programowanie sterowane zdarzeniami (częściej nazywane reaktywnym) wykracza poza programowanie interfejsy użytkownika. Przykładowo możliwe będzie zaimplementowanie dwukierunkowego wiązania w oparciu o JS, bez konieczności instalowania w tym celu frameworku typu Ractive.

    Wiązanie danych w ramach deklaratywnych

    Jednym z rozwiązań stosowanych do wiązania danych jest tzw. „brudna kontrola” (stosowana w Angularze). Po każdej zmianie danych Angular sprawdza to poprzez „pętlę podsumowania”, tj. kod, który działa w określonych odstępach czasu. Angular identyfikuje wszystkie zaobserwowane wyrażenia i wykrywa wszelkie zmiany w nich.

    Inną metodą stosowaną w Ember, Backbone i Ractive jest użycie obiektu kontenera. Framework tworzy obiekty, w których przechowywane są dane. Obiekty te mają metody dostępu do danych i za każdym razem, gdy żądasz lub zmieniasz dane, framework przechwytuje Twoje działanie i dystrybuuje informacje o nim do subskrybentów. To rozwiązanie sprawdza się znakomicie, jest wydajniejsze od brudnej kontroli i lepiej się skaluje.

    Poprawa wydajności

    Nowa metoda pozwala na monitorowanie obiektu, zmianę jego właściwości i otrzymanie raportu na jego temat. Jego zastosowanie może wyglądać następująco:

    // Model może być literałem obiektowym var zwykłyObject = ( nazwa: "Licznik", suma: 0 ); // Zdefiniuj metodę obserwatora funkcja obserwator(zmiany)( zmiany.forEach(funkcja(zmiana, i)( console.log("jaka właściwość się zmieniła? " + zmiana.nazwa); console.log("jak to się zmieniło? " + zmiana.typ); console.log("jaka jest bieżąca wartość? " + zmiana.object); console.log(change); // wszystkie zmiany )); ) // Rozpocznij oglądanie obiektu Object.observe(plainObject, obserwator);

    Zatrzymanie monitorowania obiektu odbywa się w jednej linijce kodu.

    Obiekt.unobserve(plainObject, obserwator);

    Plan rozwoju

    Jak już pisałem, natywna obsługa Object.observe() jest zaimplementowana w Chrome 36 i Node.js z włączoną flagą harmonii. W najbliższej przyszłości planowane jest także wdrożenie Wersje Opery. W innych przeglądarkach nadal możesz używać wypełnienia z biblioteki Observe-JS Polymer, która zapewnia również obsługę niektórych starszych przeglądarek.

    Wsparcie dla tej metody przewidywane jest także w frameworkach deklaratywnych – w Emberze i Ractive jest to planowane w kolejnych wydaniach, w Angularze wraz z wydaniem wydania głównego.

    Wniosek

    Przyjrzeliśmy się szczegółowo zaletom i wadom różnych podejść do przetwarzania zdarzeń i przyjrzeliśmy się jego najbliższej przyszłości. Mam nadzieję, że po przeczytaniu tego artykułu zrozumiałeś podstawowe podejścia i wzorce, które pomagają w przetwarzaniu zdarzeń i wiązaniu danych. Na przyszłość pamiętaj, że nie ma jednego najlepszego rozwiązania na wszystkie problemy.

    Ten UML opisuje, w jaki sposób interfejs prototypowy jest używany do klonowania konkretnych implementacji.

    Aby sklonować obiekt, musi istnieć konstruktor tworzący instancję pierwszego obiektu. Następnie za pomocą słowa kluczowego prototype zmienne i metody wiążą się ze strukturą obiektu.Spójrzmy na prosty przykład:

    Var TeslaModelS = funkcja() ( this.numWheels = 4; this.manufacturer = "Tesla"; this.make = "Model S"; ) TeslaModelS.prototype.go =function() ( // Obróć koła ) TeslaModelS.prototype. stop = funkcja() ( // Załóż klocki hamulcowe )

    Konstruktor umożliwia utworzenie pojedynczego obiektu TeslaModelS. Podczas tworzenia nowego obiektu TeslaModelS zachowa on stany zainicjowane w konstruktorze. Dodatkowo utrzymanie funkcji go i stop jest łatwe, ponieważ zadeklarowaliśmy je za pomocą prototype . Synonimiczny sposób rozszerzenia funkcji prototypu, jak opisano poniżej:

    Var TeslaModelS = funkcja() ( this.numWheels = 4; this.manufacturer = "Tesla"; this.make = "Model S"; ) TeslaModelS.prototype = ( go:function() ( // Obróć koła ), stop: funkcja() ( // Zastosuj klocki hamulcowe ) )

    Ujawnianie wzoru prototypowego

    Podobnie jak wzór modułu, wzór prototypu również ma odkrywczą odmianę. Wzorzec prototypu ujawniającego zapewnia hermetyzację z elementami publicznymi i prywatnymi, ponieważ zwraca literał obiektu.

    Ponieważ zwracamy obiekt, poprzedzimy obiekt prototypowy funkcją. Rozszerzając powyższy przykład, możemy wybrać, co chcemy wyeksponować w bieżącym prototypie, aby zachować ich poziomy dostępu:

    Var TeslaModelS = funkcja() ( this.numWheels = 4; this.manufacturer = "Tesla"; this.make = "Model S"; ) TeslaModelS.prototype = funkcja() ( var go = funkcja() ( // Obróć koła ); var stop = funkcja() ( // Załóż klocki hamulcowe ); return ( pressBrakePedal: stop, pressGasPedal: start ) )();

    Zwróć uwagę, że funkcje stop and go będą chronione przed zwracającym obiektem, ponieważ znajdują się poza zakresem zwracanego obiektu. Ponieważ JavaScript natywnie obsługuje dziedziczenie prototypowe, nie ma potrzeby przepisywania podstawowych funkcji.

    Często zdarza się, że jedna część aplikacji ulega zmianie, a inne wymagają aktualizacji. W AngularJS, jeśli obiekt $scope zostanie zaktualizowany, może zostać wyzwolone zdarzenie w celu powiadomienia innego komponentu. Wzorzec obserwatora właśnie to uwzględnia – jeśli obiekt zostanie zmodyfikowany, rozgłasza do obiektów zależnych informację, że nastąpiła zmiana.

    Innym doskonałym przykładem jest architektura kontrolera widoku modelu (MVC); Widok jest aktualizowany po zmianie modelu. Jedną z korzyści jest oddzielenie widoku od modelu w celu zmniejszenia zależności.

    Wikipedia

    Jak pokazano na diagramie UML, niezbędnymi obiektami są podmiot, obserwator i obiekty konkretne. W temacie znajdują się odniesienia do konkretnych obserwatorów, których należy powiadomić o ewentualnych zmianach. Obiekt Observer jest klasą abstrakcyjną, która umożliwia konkretnym obserwatorom implementację metody notify.

    Przyjrzyjmy się przykładowi AngularJS, który obejmuje wzorzec obserwatora poprzez zarządzanie zdarzeniami.

    // Kontroler 1 $scope.$on("nameChanged",function(event, args) ( $scope.name = args.name; )); ... // Kontroler 2 $scope.userNameChanged = funkcja(nazwa) ( $scope.$emit("nameChanged", (nazwa: nazwa)); );

    W przypadku wzorca obserwatora ważne jest rozróżnienie niezależnego obiektu lub podmiotu.

    Należy zauważyć, że chociaż wzorzec obserwatora oferuje wiele zalet, jedną z wad jest znaczny spadek wydajności wraz ze wzrostem liczby obserwatorów. Jednym z najbardziej znanych obserwatorów są obserwatorzy. W AngularJS możemy oglądać zmienne, funkcje i obiekty. Cykl $$digest uruchamia się i powiadamia każdego z obserwatorów o nowych wartościach za każdym razem, gdy obiekt zakresu zostanie zmodyfikowany.

    Możemy tworzyć własne podmioty i obserwatorów w JavaScript. Zobaczmy, jak to jest zaimplementowane:

    Var Temat = funkcja() ( this.observers = ; return ( subskrybujObserver: funkcja(obserwator) ( this.observers.push(obserwator); ), unsubscribeObserver: funkcja(obserwator) ( var indeks = this.observers.indexOf(obserwator) ; if(indeks > -1) ( this.observers.splice(indeks, 1); ) ), notifyObserver: funkcja(obserwator) ( var indeks = this.observers.indexOf(obserwator); if(indeks > -1) ( this.observers.notify(index); ) ), notifyAllObservers: funkcja() ( for(var i = 0; i< this.observers.length; i++){ this.observers[i].notify(i); }; } }; }; var Observer = function() { return { notify: function(index) { console.log("Observer " + index + " is notified!"); } } } var subject = new Subject(); var observer1 = new Observer(); var observer2 = new Observer(); var observer3 = new Observer(); var observer4 = new Observer(); subject.subscribeObserver(observer1); subject.subscribeObserver(observer2); subject.subscribeObserver(observer3); subject.subscribeObserver(observer4); subject.notifyObserver(observer2); // Observer 2 is notified! subject.notifyAllObservers(); // Observer 1 is notified! // Observer 2 is notified! // Observer 3 is notified! // Observer 4 is notified!

    Publikuj/subskrybuj

    Jednakże wzorzec Publikuj/Subskrybuj wykorzystuje kanał tematu/zdarzenia, który znajduje się pomiędzy obiektami chcącymi otrzymywać powiadomienia (subskrybentami) a obiektem uruchamiającym zdarzenie (wydawcą). Ten system zdarzeń pozwala w kodzie definiować zdarzenia specyficzne dla aplikacji, które mogą przekazywać niestandardowe argumenty zawierające wartości potrzebne subskrybentowi. Chodzi o to, aby uniknąć zależności pomiędzy subskrybentem a wydawcą.

    Różni się to od wzorca Observer, ponieważ każdy subskrybent implementujący odpowiednią procedurę obsługi zdarzeń w celu rejestracji i otrzymywania powiadomień o tematach nadawanych przez wydawcę.

    Wielu programistów decyduje się na agregację wzorca projektowego publikowania/subskrypcji z obserwatorem, chociaż istnieje różnica. Abonenci we wzorcu publikowania/subskrybowania są powiadamiani za pośrednictwem jakiegoś medium komunikacyjnego, ale obserwatorzy są powiadamiani poprzez implementację procedury obsługi podobnej do tematu.

    W AngularJS subskrybent „subskrybuje” wydarzenie za pomocą $on("event", wywołanie zwrotne), a wydawca "publikuje" wydarzenie za pomocą $emit("event", args) lub $broadcast("event", args) .

    Singel

    Singleton pozwala tylko na jedną instancję, ale na wiele instancji tego samego obiektu. Singleton ogranicza klientom możliwość tworzenia wielu obiektów, po utworzeniu pierwszego obiektu zwróci instancje samego siebie.

    Znalezienie przypadków użycia Singletonów jest trudne dla większości, którzy jeszcze z niego nie korzystali. Jednym z przykładów jest użycie drukarki biurowej. Jeśli w biurze jest dziesięć osób i wszyscy korzystają z jednej drukarki, dziesięć komputerów korzysta z jednej drukarki (instancji). Udostępniając jedną drukarkę, korzystają z tych samych zasobów.

    Var drukarki = (funkcja () ( var drukarkaInstance; funkcja create () ( funkcja print() ( // podstawowa mechanika drukarki ) funkcja turnOn() ( // rozgrzewanie // sprawdzanie papieru ) return ( // stany publiczne + prywatne i zachowania print: print, turnOn: turnOn ); ) return ( getInstance: funkcja() ( if(!printerInstance) ( drukarkaInstance = create(); ) zwróć drukarkęInstance; ) ); funkcja Singleton () ( if(!printerInstance) ( Instancja drukarki = inicjalizuj(); ) ); ))();

    Metoda create jest prywatna, ponieważ nie chcemy, aby klient miał do niej dostęp, jednak zauważ, że metoda uzyskac instancje metoda jest publiczna. Każdy oficer może wygenerować instancję drukarki, wchodząc w interakcję z plikiem uzyskac instancje metoda, taka:

    Var officePrinter = drukarka.getInstance();

    W AngularJS dominują singletony, z których najbardziej godnymi uwagi są usługi, fabryki i dostawcy. Ponieważ utrzymują stan i zapewniają dostęp do zasobów, utworzenie dwóch instancji mija się z celem wspólnej usługi/fabryki/dostawcy.

    Warunki wyścigu występują w aplikacjach wielowątkowych, gdy więcej niż jeden wątek próbuje uzyskać dostęp do tego samego zasobu. Singletony są podatne na warunki wyścigu, tak że jeśli żadna instancja nie została zainicjowana jako pierwsza, dwa wątki mogłyby następnie utworzyć dwa obiekty zamiast zwracać i instancję. To mija się z celem singletonu. Dlatego programiści muszą mieć uprawnienia do synchronizacji podczas wdrażania singletonów w aplikacjach wielowątkowych.

    Wniosek

    Wzorce projektowe są często używane w większych zastosowaniach, choć zrozumienie, gdzie jeden może być przewaga nad drugim, wymaga praktyki.

    Przed zbudowaniem jakiejkolwiek aplikacji należy dokładnie przemyśleć każdego aktora i sposób, w jaki współdziałają ze sobą. Po przejrzeniu wzorców projektowych Module , Prototype , Observer i Singleton powinieneś być w stanie zidentyfikować te wzorce i wykorzystać je w praktyce.