Klasy — pola, konstruktor i metody
Obiekt może nie tylko trzymać dane, ale też wykonywać działania. Zbuduj licznik doświadczenia, który pamięta swój stan między wywołaniami.
Po tej lekcji potrafisz
- Utworzysz klasę i jej instancje przez new.
- Zainicjalizujesz pola oraz użyjesz this.
- Odróżnisz metodę od pola i rozpoznasz skrócony zapis konstruktora.
Każdy bohater ma własne XP
Funkcja policzy poziom, ale kto zapamięta zebrane punkty? Klasa pozwala połączyć stan oraz działania na tym stanie. Dwie instancje korzystają z tego samego opisu, lecz mają własne wartości pól.
Małe kroki, konkretne efekty
Klasa jest przepisem na obiekt
Konstruktor działa przy new. this oznacza obiekt, na którym wykonujemy metodę. Typy pól pomagają sprawdzić przypisania.
class Gracz {
nick: string;
punkty = 0;
constructor(nick: string) { this.nick = nick; }
dodaj(ile: number): void { this.punkty += ile; }
}
const ala = new Gracz("Ala");
ala.dodaj(5);
console.log(ala.nick, ala.punkty);
Ala 5
W trybie strict pole musi dostać wartość, np. przy deklaracji lub w konstruktorze. Nie dopisuj ! do pola tylko po to, by ukryć brak inicjalizacji.
Skrócony konstruktor tworzy pole
Modyfikator public przy parametrze konstruktora jest skrótem: tworzy pole i przypisuje mu wartość argumentu. Zwykły parametr bez modyfikatora sam nie tworzy pola.
class Druzyna {
constructor(public nazwa: string, public punkty = 0) {}
opis(): string { return this.nazwa + ": " + this.punkty; }
}
console.log(new Druzyna("Wilki", 12).opis());
Wilki: 12
To składnia TypeScriptu wymagająca przekształcenia do JavaScriptu. Nasze npm start używa tsc, więc obsługuje ją tak samo jak enum.
Dwa niezależne liczniki XP
Klasa pamięta XP, a metodą oblicza poziom. Zakładamy nieujemne przyrosty punktów; ochronę stanu rozbudujemy w następnej lekcji.
class Bohater {
xp = 0;
constructor(public nick: string) {}
zdobadz(ile: number): void { this.xp += ile; }
poziom(): number { return Math.floor(this.xp / 100) + 1; }
raport(): string { return this.nick + ": poziom " + this.poziom(); }
}
const ala = new Bohater("Ala");
const neo = new Bohater("Neo");
ala.zdobadz(150);
neo.zdobadz(20);
console.log(ala.raport());
console.log(neo.raport());
Ala: poziom 2
Neo: poziom 1
Jak program dochodzi do wyniku
- Każde new tworzy osobny obiekt z własnym xp = 0.
- ala.zdobadz zmienia this.xp instancji ala, nie wszystkich bohaterów.
- poziom oblicza wynik z aktualnego stanu — nie przechowuje drugiej kopii tej informacji.
- raport wywołuje inną metodę przez this.
Zanim przejdziesz dalej
Czy const drugi = ala tworzy nowego bohatera?
Sprawdź swoje rozumowanie
Nie. Obie zmienne wskazują tę samą instancję. Nowego bohatera tworzy new Bohater(…).
Zmień kod i zobacz, co się stanie
Metoda przekazana dalej może stracić this
Zwykła metoda zależy od sposobu wywołania. Gdy przekazujesz ją jako callback, opakuj wywołanie strzałką zachowującą obiekt. Alternatywnie użyj bind.
class Licznik {
wartosc = 0;
zwieksz(): void { this.wartosc++; }
}
const licznik = new Licznik();
[1, 2, 3].forEach(() => licznik.zwieksz());
const akcja = licznik.zwieksz.bind(licznik);
akcja();
console.log(licznik.wartosc);
4
bind zwraca nową funkcję z ustalonym this. Sam zapis const akcja = licznik.zwieksz nie zachowuje powiązania z obiektem.
Gdy wynik zaskakuje
| Pomyłka | Co się dzieje i jak to poprawić |
|---|---|
| Brak this przed polem | Samo xp szuka lokalnej zmiennej. this.xp odczytuje pole obiektu. |
| Konstruktor z typem zwracanym | Konstruktor nie dostaje adnotacji : void ani : Bohater. |
| Wspólna referencja uznana za nową instancję | Przypisanie zmiennej nie uruchamia konstruktora. |
Teraz Twoja kolej
Rozwiązanie jest jedną z możliwości. Zanim je odkryjesz, uruchom własną wersję dla podanych danych i przynajmniej jednego przypadku brzegowego.
Stwórz Licznik z wartością 0 i metodą dodaj. Wywołaj ją dwukrotnie i wypisz 2.
Mała podpowiedź
Metoda modyfikuje this.wartosc.
Przykładowe rozwiązanie z wyjaśnieniem
class Licznik {
wartosc = 0;
dodaj(): void { this.wartosc++; }
}
const l = new Licznik();
l.dodaj(); l.dodaj();
console.log(l.wartosc);
2
Obiekt pamięta efekt poprzedniego wywołania.
Klasa Przedmiot ma nazwę i cenę podane konstruktorowi. Metoda opis zwraca „Latarka: 20 zł”.
Mała podpowiedź
Użyj skróconego konstruktora public.
Przykładowe rozwiązanie z wyjaśnieniem
class Przedmiot {
constructor(public nazwa: string, public cena: number) {}
opis(): string { return this.nazwa + ": " + this.cena + " zł"; }
}
console.log(new Przedmiot("Latarka", 20).opis());
Latarka: 20 zł
Opis jest wynikiem metody, a nie osobnym polem wymagającym synchronizacji.
Utwórz dwa obiekty z polem monety. Dodanie 10 do pierwszego nie może zmienić drugiego.
Mała podpowiedź
Potrzebujesz dwóch wywołań new.
Przykładowe rozwiązanie z wyjaśnieniem
class Sejf {
monety = 0;
dodaj(n: number): void { this.monety += n; }
}
const a = new Sejf();
const b = new Sejf();
a.dodaj(10);
console.log(a.monety, b.monety);
10 0
Różne instancje mają różne pola.
Klasa Trening ma tablicę wyników, metodę dodaj oraz suma. Dodaj 10 i 15, wypisz 25. Nowy trening ma sumę 0.
Mała podpowiedź
Inicjalizuj tablicę przy deklaracji pola.
Przykładowe rozwiązanie z wyjaśnieniem
class Trening {
wyniki: number[] = [];
dodaj(n: number): void { this.wyniki.push(n); }
suma(): number {
let razem = 0;
for (const n of this.wyniki) { razem += n; }
return razem;
}
}
const t = new Trening();
t.dodaj(10); t.dodaj(15);
console.log(t.suma(), new Trening().suma());
25 0
Tablica należy do instancji i powstaje osobno dla każdego new.
Co zabierasz do następnej lekcji
- new tworzy instancję, konstruktor ustawia jej początkowy stan.
- this wskazuje obiekt używany w danym wywołaniu.
- Klasa istnieje w JavaScripcie; interface po kompilacji znika.