Kompletny przewodnik z przykładami

JavaScript to wszechstronny język programowania, który pozwala na tworzenie aplikacji w oparciu o różnorodne paradygmaty, w tym programowanie funkcyjne, obiektowe oraz imperatywne.

W celu wsparcia podejścia obiektowego, JavaScript oferuje mechanizm klas. Zrozumienie ich działania jest kluczowe, dlatego ten artykuł stanowi kompleksowy przewodnik po klasach JavaScript, wyjaśniając ich istotę i sposób użycia.

Czym są klasy w JavaScript?

W programowaniu obiektowym, systemy są modelowane jako zbiory obiektów, które wchodzą ze sobą w interakcje. Obiekty, aby funkcjonować, przechowują dane w atrybutach (właściwościach) i realizują działania za pomocą metod. Klasa pełni rolę definicji, określając, jakie cechy i działania posiadają obiekty danego typu. W związku z tym klasy są swoistymi planami, na podstawie których tworzone są obiekty.

Terminologia związana z klasami

Aby upewnić się, że posługujemy się jednolitym językiem, poniżej znajduje się objaśnienie kluczowych terminów, które będą używane w tym artykule. Jeżeli posiadasz już wiedzę na temat programowania obiektowego, możesz przejść do kolejnej sekcji.

❇️ Klasa to schemat obiektu. Stanowi szablon, z którego mogą być tworzone obiekty tego samego rodzaju. Proces kreowania obiektu na podstawie szablonu zdefiniowanego przez klasę nazywany jest instancjonowaniem.

❇️ Członek klasy to każdy element przynależący do klasy. Wyróżniamy dwa rodzaje członków: metody i atrybuty (właściwości).

❇️ Atrybut, inaczej właściwość, to element klasy, który służy do przechowywania wartości. Mogą to być zarówno proste dane, takie jak liczby czy ciągi znaków, jak i bardziej złożone obiekty lub tablice.

❇️ Niektóre atrybuty są dostępne wyłącznie wewnątrz klasy i nazywane są atrybutami prywatnymi. Inne są dostępne zarówno wewnątrz, jak i na zewnątrz klasy, i są to atrybuty publiczne.

❇️ Metoda to funkcja zdefiniowana w obrębie klasy. Jest ona więc związana z klasą i ma dostęp do atrybutów publicznych i prywatnych. Podobnie jak atrybuty, metody również mogą być publiczne lub prywatne.

❇️ Istnieją metody, które udostępniają interfejs dla kodu spoza klasy, umożliwiając interakcję z atrybutami klasy. Dzielą się one na dwie kategorie: gettery i settery. Gettery służą do pobierania wartości atrybutów, natomiast settery do ich modyfikowania.

❇️ Niektóre elementy klasy są statyczne. Oznacza to, że są one dostępne bezpośrednio w klasie i nie można do nich odwoływać się poprzez instancje klasy.

Natomiast elementy niestatyczne są dostępne wyłącznie poprzez instancje klasy. Zanim będzie możliwe odwołanie się do niestatycznego członka klasy, konieczne jest utworzenie instancji tej klasy.

Podczas tworzenia instancji klasy wywoływana jest specjalna metoda, której zadaniem jest ustalenie początkowych wartości atrybutów nowej instancji. Ta metoda to konstruktor.

Wyjaśnienie procesu instancjonowania klasy

W JavaScript, aby utworzyć instancję klasy, stosujemy słowo kluczowe `new` w połączeniu z nazwą klasy. Przykładowo, instancję klasy `Array` tworzymy w następujący sposób:

const myArr = new Array()

Tworzenie klas w JavaScript

W tej sekcji przyjrzymy się procesowi tworzenia klasy, która będzie wykorzystywać wszystkie koncepcje omówione w sekcji poświęconej terminologii. Zrobimy to, analizując kolejne przykłady, gdzie każdy następny przykład bazuje na poprzednich.

Deklarowanie pustej klasy

Aby zdefiniować klasę w JavaScript, używamy słowa kluczowego `class`, a następnie nadajemy klasie nazwę. Kolejnym krokiem jest zdefiniowanie ciała klasy, które umieszczamy w nawiasach klamrowych. W ciele klasy znajdują się wszystkie jej elementy.

Poniżej znajduje się przykład deklaracji klasy z pustym ciałem:

class Dog {

}

Teraz możemy utworzyć instancję tej klasy w następujący sposób i wyświetlić ją w konsoli:

const pet = new Dog;
console.log(pet);

Tworzenie atrybutów publicznych

Atrybuty publiczne definiuje się poprzez podanie nazwy i opcjonalnie wartości początkowej.

class Dog {
    name = "Roy";
    age;
}

W tym przykładzie zdefiniowaliśmy atrybut `name` z przypisaną wartością tekstową oraz atrybut `age` bez wartości.

const pet = new Dog();

console.log(pet.name);
console.log(pet.age);

Definiowanie metod publicznych

Metody możemy dodawać do naszej klasy wewnątrz jej ciała. Definiujemy je w taki sam sposób, jak funkcje, pomijając jednak słowo kluczowe `function`.

class Dog {
    name = "Roy";
    age;

    walk () {
        console.log("Walking");
    }
}

W powyższym przykładzie zdefiniowaliśmy metodę `walk`. Każda instancja klasy `Dog` będzie posiadać tę metodę.

const pet = new Dog();
pet.walk();

Dostęp do atrybutów z metod

W JavaScript dostęp do atrybutów obiektu zazwyczaj uzyskuje się za pomocą operatora kropki. Na przykład, jeśli posiadamy obiekt o nazwie `person` i chcemy odczytać atrybut `name`, zrobimy to w następujący sposób:

person.name

Jeśli jednak chcemy odwołać się do atrybutu w obrębie obiektu, zamiast nazwy obiektu używamy słowa kluczowego `this`. Oto przykład:

this.name

Słowo kluczowe `this` odnosi się do aktualnego obiektu. Zatem, aby odczytać atrybut klasy w jej metodach, użyjemy składni `this.nazwaAtrybutu`.

Tworzenie atrybutów prywatnych

Załóżmy, że chcemy, aby atrybuty `name` i `age` były prywatne. W takim przypadku klasę należy zdefiniować w następujący sposób:

class Dog {
    #name = "Roy";
    #age;

    walk () {
        console.log("Walking");
    }
}

Jak widać, atrybuty prywatne poprzedzamy znakiem `#`. Próba odczytania tych atrybutów bezpośrednio spowoduje wystąpienie błędu.

const dog = new Dog();

dog.#name

Tworzenie metod pobierających i ustawiających

W tym momencie atrybuty `name` i `age` klasy są prywatne, więc dostęp do nich możliwy jest tylko za pomocą metod zdefiniowanych wewnątrz klasy.

Aby umożliwić kodowi spoza klasy dostęp do tych atrybutów, definiujemy metody pobierające (gettery) i ustawiające (settery). Zróbmy to dla atrybutu `name`.

class Dog {
    #name = "Roy";
    #age;

    get name () {
        return this.#name;
    }

    set name (value) {
        this.#name = value;
    }

    walk () {
        console.log("Walking");
    }
}

Mając zdefiniowaną powyżej klasę, możemy ustawić nazwę i wyświetlić ją za pomocą poniższego kodu:

const pet = new Dog();

// Setting the name
pet.name = "Rex";

// Getting the name
console.log(pet.name);

Tworzenie metod prywatnych

Podobnie jak atrybuty prywatne, metody prywatne również poprzedzamy znakiem `#`. Zatem deklaracja metody prywatnej wyglądałaby następująco:

class Dog {
    #name = "Roy";
    #age;

    get name () {
        return this.#name;
    }

    set name (value) {
        this.#name = value;
    }

    #increaseAge() {
        this.#age ++;
    }

    #decreaseAge () {
        this.#age --;
    }

    walk () {
        console.log("Walking");
    }
}

Próba wywołania tych metod spoza klasy zakończy się niepowodzeniem.

const pet = new Dog();
pet.#increaseAge();

Tworzenie metody konstruktora

Możemy również zdefiniować metodę konstruktora. Ta metoda jest wywoływana automatycznie za każdym razem, gdy tworzona jest nowa instancja klasy. Konstruktora możemy wykorzystać do inicjalizacji atrybutów obiektu. W tym przykładzie zainicjujemy wiek i imię wartościami, które użytkownik przekaże podczas tworzenia instancji.

class Dog {
    #name;
    #age;

    constructor (name = "Dog", age = 0) {
        this.#name = name;
        this.#age = age;
    }

    get name () {
        return this.#name;
    }

    set name (value) {
        this.#name = value;
    }

    #increaseAge() {
        this.#age ++;
    }

    #decreaseAge () {
        this.#age --;
    }

    walk () {
        console.log("Walking");
    }
}

Podczas tworzenia instancji naszej klasy, możemy podać imię i wiek psa.

const pet = new Dog('Roy', 3);
console.log(pet.name);

Tworzenie atrybutów i metod statycznych

Jak już wspomniano, dostęp do elementów statycznych można uzyskać bez konieczności tworzenia instancji klasy. W poniższym przykładzie utworzymy statyczny atrybut i metodę.

class Dog {
    #name;
    #age;
    static genus = "Canis";

    constructor (name = "Dog", age = 0) {
        this.#name = name;
        this.#age = age;
    }

    static bark() {
        console.log("Woof");
    }

    get name () {
        return this.#name;
    }

    set name (value) {
        this.#name = value;
    }

    #increaseAge() {
        this.#age ++;
    }

    #decreaseAge () {
        this.#age --;
    }

    walk () {
        console.log("Walking");
    }
}

Teraz możemy uzyskać dostęp do statycznego atrybutu i metody bezpośrednio poprzez klasę, bez tworzenia jej instancji.

console.log(Dog.genus);
Dog.bark();

Dziedziczenie

Klasy mogą dziedziczyć atrybuty z innych klas. Klasa, która dziedziczy elementy z innej klasy, nazywana jest podklasą, natomiast klasa, z której się dziedziczy, jest klasą bazową lub nadklasą.

Aby utworzyć podklasę w JavaScript, używamy słowa kluczowego `extends`. Oto przykład, w którym dziedziczymy z klasy `Dog`.

class Rottweiler extends Dog {
    constructor (name, age) {
        super(name, age);
        this.breed = 'rottweiler';
    }
}

Jak widać, klasa jest w dużej mierze taka sama jak wcześniej, jednak w konstruktorze wywołaliśmy funkcję `super`. Słowo kluczowe `super` odwołuje się do konstruktora klasy bazowej. Zatem wywołaliśmy konstruktor klasy `Dog` wewnątrz konstruktora `Rottweiler`, przekazując mu nazwę i wiek psa.

const myPet = new Rottweiler();
console.log(myPet);

Podsumowanie

W niniejszym artykule omówiliśmy zagadnienie klas w JavaScript. Wyjaśniliśmy, czym one są, jakie elementy mogą zawierać oraz jak klasyfikujemy poszczególne elementy. Całość zilustrowaliśmy przykładami.

Następnie możesz zapoznać się z pytaniami dotyczącymi programowania obiektowego na rozmowach kwalifikacyjnych.


newsblog.pl