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.

CEL LEKCJI

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.
WPROWADZENIE

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.

POZNAJ SKŁADNIĘ

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.

src/index.ts — osobny przykład
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);
wynik w konsoli
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.

src/index.ts — osobny przykład
class Druzyna {
  constructor(public nazwa: string, public punkty = 0) {}
  opis(): string { return this.nazwa + ": " + this.punkty; }
}
console.log(new Druzyna("Wilki", 12).opis());
wynik w konsoli
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.

PRZYKŁAD Z OMÓWIENIEM

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.

src/index.ts — osobny przykład
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());
wynik w konsoli
Ala: poziom 2
Neo: poziom 1

Jak program dochodzi do wyniku

  1. Każde new tworzy osobny obiekt z własnym xp = 0.
  2. ala.zdobadz zmienia this.xp instancji ala, nie wszystkich bohaterów.
  3. poziom oblicza wynik z aktualnego stanu — nie przechowuje drugiej kopii tej informacji.
  4. 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(…).

EKSPERYMENT

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.

src/index.ts — osobny przykład
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);
wynik w konsoli
4

bind zwraca nową funkcję z ustalonym this. Sam zapis const akcja = licznik.zwieksz nie zachowuje powiązania z obiektem.

CZĘSTE BŁĘDY

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.
ZADANIA

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.

ZAD 1Licznik kliknięć w konsoli★☆☆

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
src/index.ts — osobny przykład
class Licznik {
  wartosc = 0;
  dodaj(): void { this.wartosc++; }
}
const l = new Licznik();
l.dodaj(); l.dodaj();
console.log(l.wartosc);
wynik w konsoli
2

Obiekt pamięta efekt poprzedniego wywołania.

ZAD 2Karta przedmiotu★★☆

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
src/index.ts — osobny przykład
class Przedmiot {
  constructor(public nazwa: string, public cena: number) {}
  opis(): string { return this.nazwa + ": " + this.cena + " zł"; }
}
console.log(new Przedmiot("Latarka", 20).opis());
wynik w konsoli
Latarka: 20 zł

Opis jest wynikiem metody, a nie osobnym polem wymagającym synchronizacji.

ZAD 3Dwa sejfy★★☆

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
src/index.ts — osobny przykład
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);
wynik w konsoli
10 0

Różne instancje mają różne pola.

ZAD 4Trener serii★★★

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
src/index.ts — osobny przykład
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());
wynik w konsoli
25 0

Tablica należy do instancji i powstaje osobno dla każdego new.

PODSUMOWANIE

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.