Konstruktor

Tak jak już sobie powiedzieliśmy, w Javascript mamy kilka typów danych. Większość z nich możemy tworzyć na dwa sposoby: korzystając z tak zwanego literału (skrócony zapis) lub na bazie tak zwanych konstruktorów:

const obA = {}
const obB = new Object();

const boolA = true;
const boolB = new Boolean(true);

const tabA = ["ala", "bala"];
const tabB = new Array("ala", "bala");

const txtA = "Ala ma Konczenti";
const txtB = new String("Ala ma Konczenti");

Każdy taki typ charakteryzuje się tym, że możemy dla niego odpalać różne właściwości i metody. Ogólnie jest więc bytem, który "zachowuje się w jakiś sposób".

My jako programiści możemy też tworzyć własne typy.

W wielu językach do tworzenia własnych typów obiektów korzystamy z tak zwanych klas (class). Klasy są formą wzoru, szablonu, która definiuje właściwości i funkcje, które trafią do obiektów tworzonych na jej podstawie.

W Javascript wymyślono to ciut inaczej, ponieważ składni class nie było, natomiast dane typy tworzyliśmy na podstawie zwykłych funkcji, które tutaj nazwano konstruktorami. Wraz z rozwojem języka wprowadzono też inne sposoby tworzenia obiektów, w tym wspomnianą składnię class, która nie tylko uprościła nam pisanie kodu, ale też ułatwiła niektóre czynności (plus dodała kilka nowych możliwości).

W dzisiejszych czasach z pewnością będziesz używał nowszej składni. Nie oznacza to jednak, że nie warto poznać poniższej składni. Poniższy sposób tworzenia obiektów spotkasz nie jeden raz, a i w obu przypadkach zasada działania będzie praktycznie ta sama (z pewnymi wyjątkami). Różnice będą głównie w sposobie zapisu.

Tworzenie konstruktora

Stwórzmy przykładowy konstruktor, na bazie którego stworzymy nowe obiekty:

Jak widzisz konstruktor to zwykła funkcja i w zasadzie każda funkcja może stać się naszym szablonem. Wyjątkiem tutaj są funkcje strzałkowe, których w tym przypadku użyć nie możemy.

Nazwę funkcji tak samo jak nazwy klas piszemy z dużej litery - to tylko konwencja ułatwiająca nam późniejszą pracę.

Aby teraz utworzyć nowe obiekty na bazie takiego szablonu (podobnie jak to było w przypadku innych obiektów) skorzystamy ze słowa kluczowego new:

Prototyp

Gdy tworzysz pojedynczy obiekt jakiegoś typu, dostaje on właściwość [[Prototype]], która wskazuje prototyp danego obiektu. Na ten sam obiekt wskazuje też właściwość prototype w konstruktorze danego typu. Zasada ta tyczy się każdego typu danych:

To samo dzieje się w przypadku naszych własnych typów.

Żeby dokładniej zbadać powyższy przypadek, wypiszmy w konsoli za pomocą console.dir naszą funkcję:

Jak zobaczysz w konsoli funkcja także jest obiektem - w tym przypadku typu Function. Obowiązuje ją więc ta sama zasada co inne typy, czyli dostała właściwość [[prototype]], która wskazuje na prototyp wszystkich funkcji, a na który wskazuje też właściwość Function.prototype. Dzięki temu dla każdej funkcji możemy wykonać kilka dodatkowych operacji.

Poza tym nasza funkcja ma kilka innych właściwości takich jak name (nazwa funkcji), arguments (przekazane wartości), caller (funkcja, która wywołała aktualną funkcję), length (długość), oraz prototype.

Gdy zbudujemy nowy obiekt na bazie naszej funkcji, dostanie on właściwość [[Prototype]], która będzie wskazywać właśnie na tą ostatnią właściwość, która stanie się jego prototypem.

Rozbudowa prototypu

Początkowo nasz prototyp jest praktycznie pusty, bo ma w sobie tylko 2 właściwości: constructor oraz [[Prototype]]. Właściwość constructor wskazuje na funkcję na bazie której został utworzony dany obiekt. Właściwość [[Prototype]] wskazuje na prototyp tego prototypu (patrz poprzedni rozdział).

Prototyp jest obiektem, więc bez problemu możemy mu dodawać nowe metody i właściwości tak samo jak to robiliśmy z innymi obiektami.

Dodając lub odejmując do prototypu jakąś funkcjonalność, nie musisz ręcznie aktualizować wcześniej utworzonych obiektów, ponieważ wszystkie one odwołują się do tego samego miejsca.

Rozszerzanie wbudowanych typów

Nie tylko naszym własnym typom możemy modyfikować prototyp. Podobnie możemy ruszyć obiekty będące prototypami typów już wbudowanych.

Nie jest to jednak do końca polecana praktyka (zwaną potocznie "monkey patching").

Właśnie przez takie rozbudowywanie wbudowanych typów jakiś czas temu w świecie JavaScript pojawiły się kontrowersje. Znana biblioteka MooTools rozszerzała tablice o własne metody. W pewnym momencie twórcy JavaScript chcieli wprowadzić swoje metody o takich nazwach i nagle zostali postawieni pod ścianą. Gdy wprowadzą dane metody, strony działające w oparciu o MooTools mogły by przestać działać lub działały by błędnie. Z drugiej strony gdy pójdą na kompromis okaże się, że nowo wprowadzane metody będą miały udziwnione nazwy...

Podsumowując. Własne typy rozwijaj do woli. Z rozwijaniem wbudowanych typów uważaj. Jeżeli robisz bibliotekę, która ma dogonić popularnością jQuery, raczej bym nie modyfikował domyślnych typów... Ewentualnie zrobił bym własne typy na bazie tych wbudowanych. Ale do tego raczej sięgnął bym po klasy, które omówimy w kolejnym rozdziale.

Last updated