Składowe statyczne należą do klasy, nie do instancji
Większość metod, które piszesz w klasie, działa na konkretnej instancji: na określonym user, na określonym circle. Składowe statyczne są inne. Należą do samej klasy. Wywołujesz je na nazwie klasy, a nie na instancji.
double żyje w MathUtils, a nie w jej instancjach. Utworzenie instancji nic nie da: m.double to undefined. To odwrotność zwykłych metod, które żyją w instancjach (ściśle mówiąc, w prototypie) i są niewidoczne dla samej klasy.
Model myślowy: słowo kluczowe class tworzy dwie rzeczy. Zestaw metod instancji (używanych przez new MathUtils()) i zestaw metod statycznych (używanych bezpośrednio przez MathUtils). Słowo kluczowe static decyduje, do której szuflady trafia dana składowa.
Klasyczne zastosowanie: metody factory
Najczęstszy powód, by napisać metodę statyczną, to alternatywny konstruktor. Prawdziwy constructor przyjmuje takie argumenty, jakie przyjmuje, ale często chcesz mieć inne sposoby na zbudowanie obiektu: z JSON-a, z wiersza bazy danych, z adresu URL.
fromJSON nie używa użytkownika, tylko go tworzy. Właśnie wtedy metoda statyczna pasuje najlepiej. Alternatywą byłaby luźna funkcja parseUser, ale trzymanie jej w klasie grupuje powiązane zachowanie i od razu pokazuje intencję w miejscu wywołania.
Właściwości statyczne
Dane możesz też przypiąć do samej klasy:
Circle.PI jest wspólne dla wszystkich kół: to stała klasy, a nie kopia zapisywana w każdej instancji. W metodach instancji odwołujesz się do niej przez nazwę klasy (Circle.PI), a nie przez this.
Właściwości statyczne przydają się do konfiguracji, pamięci podręcznych współdzielonych przez instancje, liczników i stałych na poziomie klasy.
this w metodzie statycznej to klasa
W zwykłej metodzie this to instancja. W metodzie statycznej this to klasa:
this.count wewnątrz increment oznacza Counter.count. Na początku może to wydawać się dziwne, ale właśnie dzięki temu działa dziedziczenie metod statycznych: this wskazuje na klasę, na której wywołano metodę, a nie na tę, która ją zdefiniowała.
Metody statyczne i dziedziczenie
Podklasy dziedziczą metody statyczne. A ponieważ this wskazuje na klasę, na której wywołano metodę, metody factory same tworzą właściwą podklasę:
Animal.create używa new this(name). Gdy wywołujesz Dog.create("Rex"), this to Dog, więc new this(name) buduje obiekt Dog. Gdyby w tym miejscu stało new Animal(name), zawsze powstawałby Animal i wzorzec by się rozsypał. To główny powód, dla którego this w metodach statycznych wskazuje na klasę.
Static a instancja: porównanie na przykładzie
Oto ta sama logika zapisana w obu stylach:
Obie liczą to samo. Wersja instancji czyta dane z this.celsius. Wersja statyczna dostaje dane wejściowe jako argument. Wybierz metodę instancji, gdy operacja to "coś, co robi ten obiekt". Wybierz metodę statyczną, gdy to "coś, co ta klasa umie policzyć na podstawie danych wejściowych".
Bloki statyczne do inicjalizacji
Czasem inicjalizacja właściwości statycznej wymaga czegoś więcej niż jednego wyrażenia: pętli, warunku, kilku zależnych od siebie wartości. Do tego służą bloki statyczne:
Blok static { ... } wykonuje się raz, w chwili definiowania klasy. Wewnątrz niego this to klasa. Używaj go do inicjalizacji w kilku krokach. Przy pojedynczym przypisaniu zwykłe pole statyczne jest czytelniejsze.
Prywatne składowe statyczne
Pola statyczne też mogą być prywatne, dzięki prefiksowi #. Dostęp do nich jest tylko z wnętrza klasy:
#nextId jest zamknięte wewnątrz klasy. Kod z zewnątrz może wywołać IdGenerator.next(), ale nie może dotknąć licznika ani go wyzerować. Pola prywatne dostaną wkrótce własną stronę, ale warto już teraz wiedzieć, że połączenie static i # działa.
Kiedy nie sięgać po static
Metody statyczne to wygodny sposób na grupowanie funkcji pomocniczych, ale to nie powód, by każde narzędzie zamieniać w klasę. Jeśli masz plik pełen niezależnych funkcji, eksportuj funkcje. Nie opakowuj ich w klasę z samymi metodami statycznymi tylko po to, by nadać im przestrzeń nazw. Moduł już to robi, i to czyściej.
Sięgaj po static, gdy:
- Funkcja naprawdę należy do klasy (factory, konwerter, walidator dla tego typu).
- Potrzebujesz stanu współdzielonego przez wszystkie instancje tej klasy.
- Podklasa może chcieć nadpisać albo odziedziczyć to zachowanie.
W pozostałych przypadkach zwykła funkcja jest prostszym narzędziem.
Dalej: pola prywatne
Mignęło ci #nextId: to składnia pól prywatnych w JavaScript. Działa zarówno dla składowych instancji, jak i statycznych, i jest nowoczesnym sposobem na ukrycie szczegółów implementacji wewnątrz klasy. O tym w następnej części.
Najczęściej zadawane pytania
Czym jest metoda statyczna w JavaScript?
Metoda statyczna należy do samej klasy, a nie do jej instancji. Deklarujesz ją słowem kluczowym static i wywołujesz na klasie: MyClass.doThing(). Instancje nie mają do niej dostępu przez this.doThing(), ma go tylko klasa.
Kiedy użyć metody statycznej zamiast metody instancji?
Gdy funkcja jest związana z klasą, ale nie musi czytać ani zmieniać konkretnej instancji. Typowe przypadki to metody factory, takie jak User.fromJSON(...), narzędzia w stylu Math.max oraz stałe, które należą do klasy traktowanej jak przestrzeń nazw. Jeśli metoda potrzebuje, żeby this wskazywało na instancję, to jest metoda instancji.
Czy metody statyczne mają dostęp do właściwości instancji?
Nie bezpośrednio. Wewnątrz metody statycznej this wskazuje na klasę, a nie na instancję, więc this.name czyta właściwość statyczną, a nie pole instancji. Jeśli potrzebujesz danych instancji, przekaż ją jako argument: static summarize(user) { return user.name; }.
Czy metody statyczne są dziedziczone w JavaScript?
Tak. Gdy podklasa rozszerza klasę nadrzędną, statyczne metody rodzica są dostępne także w podklasie. Wewnątrz metody statycznej this wskazuje na tę klasę, na której ją wywołano, i właśnie dzięki temu statyczne metody factory działają poprawnie w podklasach.