Konstruktor w JavaScript, często opisywany skrótem js constructor, to mechanizm tworzenia i inicjalizowania obiektów. W praktyce decyduje o tym, jak powstaje instancja, gdzie trafiają dane startowe i co stanie się, gdy zapomnisz o new. Poniżej rozkładam temat na czynniki pierwsze: od podstaw, przez różnice między klasami i funkcjami, aż po pułapki, które najczęściej łapią początkujących.
Najważniejsze rzeczy o konstruktorze w JavaScript
- Konstruktor inicjalizuje obiekt, a sam obiekt tworzy zwykle operator
new. - W klasie możesz mieć tylko jeden
constructor, a przy dziedziczeniu trzeba pamiętać osuper(). - Nie każda funkcja jest konstruktorem; arrow function nie zadziała z
new. -
constructorjako metoda klasy iObject.prototype.constructorto dwie różne rzeczy. - W prostych przypadkach funkcja fabrykująca bywa czytelniejsza niż klasa.

Jak konstruktor tworzy obiekt i co dzieje się pod spodem
Konstruktor nie jest „magiczny” sam z siebie. To po prostu specjalna funkcja albo metoda, która przygotowuje obiekt do użycia: przypisuje właściwości, ustawia stan początkowy i często wspiera wspólne zachowanie wielu instancji. Najlepiej widać to w klasie, bo zapis jest czytelny i od razu pokazuje, co trafia do nowego obiektu.
class User {
constructor(name, role = "user") {
this.name = name;
this.role = role;
}
greet() {
return `Cześć, ${this.name}`;
}
}
const anna = new User("Anna");
W tym przykładzie constructor bierze dane wejściowe i zapisuje je na this. Dzięki temu każda nowa instancja dostaje własny stan, ale sama logika tworzenia zostaje w jednym miejscu. To dobra baza, jeśli obiekty mają być spójne i łatwe do rozbudowy.
Żeby dobrze używać konstruktora, trzeba jednak rozróżnić trzy podobne, ale nieidentyczne pojęcia: klasę, funkcję konstrukcyjną i samą właściwość constructor. Bez tego łatwo pomylić składnię z faktycznym zachowaniem kodu, a wtedy błędy pojawiają się w najmniej wygodnym momencie. Do tego właśnie przechodzę w następnej sekcji.
Konstruktor w klasie, funkcja konstrukcyjna i właściwość constructor
W kodzie spotkasz trzy rzeczy, które brzmią podobnie, ale pełnią inne role. To ważne, bo początkujący często wrzucają je do jednego worka, a potem dziwią się, że obj.constructor nie jest tym samym co metoda constructor w klasie.
| Pojęcie | Gdzie występuje | Co oznacza |
|---|---|---|
constructor w klasie |
wewnątrz class
|
metoda wywoływana przy tworzeniu instancji |
| Funkcja konstrukcyjna | zwykła funkcja używana z new
|
starszy sposób tworzenia obiektów |
obj.constructor |
na obiektach | referencja do funkcji, która stworzyła obiekt |
Jeśli klasa nie ma własnego konstruktora, JavaScript tworzy prosty wariant domyślny. Gdy klasa dziedziczy po innej, konstruktor potomka musi zacząć od super(), bo bez tego obiekt nie zostanie poprawnie zainicjalizowany. W praktyce to jeden z najważniejszych momentów, w których konstruktor przestaje być „tylko funkcją”, a staje się elementem całego modelu obiektowego.
Warto też pamiętać, że w klasie może istnieć tylko jeden constructor. Jeśli spróbujesz zdefiniować drugi, dostaniesz błąd składni. To ograniczenie jest sensowne: JavaScript oczekuje jednego jasnego miejsca inicjalizacji, a nie kilku konkurujących wersji startu obiektu.
Jak działa `new` krok po kroku
Operator new robi więcej, niż wygląda na pierwszy rzut oka. To właśnie on uruchamia cały mechanizm tworzenia instancji, dlatego bez zrozumienia jego roli konstruktor pozostaje tylko składnią. Ja traktuję new jako sygnał: „stwórz nowy obiekt i przygotuj go według reguł tej funkcji”.
- Tworzy nowy, pusty obiekt.
- Ustawia jego prototyp tak, aby mógł korzystać ze wspólnych metod.
- Wywołuje konstruktor z
thiswskazującym na ten świeży obiekt. - Zwraca obiekt, chyba że konstruktor jawnie odda inny obiekt.
function Person(name) {
this.name = name;
}
const a = new Person("Ola"); // działa poprawnie
const b = Person("Ola"); // w trybie strict this nie oznacza nowego obiektu
Jeżeli wywołasz funkcję konstrukcyjną bez new, efekt zależy od trybu i rodzaju funkcji. W klasach dostaniesz błąd od razu, co akurat jest korzystne, bo szybciej widzisz problem. W starszym kodzie takie pomyłki bywają bardziej zdradliwe, bo nie zawsze wybuchają w miejscu, w którym je popełniłeś.
To prowadzi do jeszcze jednego praktycznego szczegółu: wbudowane konstruktory nie zawsze zachowują się identycznie. Przy API standardowym zawsze sprawdzam dokumentację konkretnego obiektu, zamiast zakładać, że każda nazwa zakończona na „constructor” działa dokładnie tak samo. I właśnie te wbudowane typy spotykasz w codziennej pracy częściej, niż wielu osobom się wydaje.
Wbudowane konstruktory, z których korzystasz bez zastanowienia
Najczęściej pracuje się nie z własnymi klasami, tylko z gotowymi konstruktorami dostarczanymi przez język. To one tworzą podstawowe struktury danych i obiekty systemowe, które są fundamentem większości aplikacji webowych.
| Konstruktor | Do czego służy | Kiedy się przydaje |
|---|---|---|
Array |
tworzy tablice | gdy pracujesz na uporządkowanych listach danych |
Object |
tworzy zwykłe obiekty | gdy potrzebujesz prostego modelu danych |
Date |
przechowuje daty i czas | gdy liczysz terminy, godziny lub różnice czasowe |
Map |
przechowuje pary klucz-wartość | gdy klucze nie muszą być zwykłymi stringami |
Set |
przechowuje unikalne wartości | gdy chcesz szybko usuwać duplikaty |
RegExp |
tworzy wyrażenia regularne | gdy wzorzec powstaje dynamicznie |
Praktyczna różnica między nimi nie polega tylko na nazwie. Map i Set zwykle dają czytelniejszy kod niż wciskanie wszystkiego do zwykłego obiektu, a Date przypomina, że nie każdy konstruktor służy do budowania własnych modeli danych. Właśnie dlatego warto znać tę grupę osobno, a nie traktować jej jak przypadkowy katalog funkcji.
Gdy rozumiesz już, co tworzy język, dużo łatwiej zauważyć typowe błędy. A tych przy konstruktorach jest kilka, i niektóre potrafią kosztować więcej czasu niż sam kod, który napisałeś.
Najczęstsze błędy, które psują konstruktor
-
Wywołanie bez
new- przy funkcji konstrukcyjnej obiekt może nie powstać tak, jak oczekujesz, athisprzestaje oznaczać nową instancję. -
Arrow function jako konstruktor - taka funkcja nie działa z
new, więc nie nadaje się do tworzenia instancji. -
thisprzedsuper()- w klasie dziedziczącej najpierw musisz wywołaćsuper(), dopiero potem korzystać zthis. -
Mylenie metody
constructorz właściwościąobj.constructor- to podobne nazwy, ale zupełnie inne role. - Definiowanie metod wewnątrz konstruktora bez potrzeby - wtedy każda instancja dostaje własną kopię funkcji, co zwykle tylko mnoży pamięć i utrudnia utrzymanie.
class Admin extends User {
constructor(name, level) {
super(name);
this.level = level;
}
}Ten fragment pokazuje najważniejszą zasadę dziedziczenia: w klasie potomnej najpierw uruchamiasz konstruktora rodzica, a dopiero potem dopinasz własne pola. Jeśli ten porządek zostanie naruszony, obiekt nie zainicjalizuje się poprawnie. Gdy unikniesz tych pułapek, decyzja sprowadza się już głównie do wyboru między klasą a funkcją fabrykującą.
Kiedy lepiej użyć klasy, a kiedy funkcji fabrykującej
Ja zwykle wybieram klasę wtedy, gdy obiekt ma wiele wspólnych metod i sensownie wpisuje się w model domenowy. Funkcję fabrykującą wolę wtedy, gdy zależy mi na prostym API, prywatnym stanie i mniejszej ceremonii przy tworzeniu obiektów. To nie jest spór ideologiczny, tylko decyzja o tym, co będzie łatwiejsze do utrzymania za kilka miesięcy.
| Rozwiązanie | Mocne strony | Ograniczenia | Kiedy wybrać |
|---|---|---|---|
| Klasa z konstruktorem | czytelna struktura, extends, super(), wspólne metody na prototypie |
bardziej sztywna inicjalizacja, mniej swobody w niestandardowych wariantach | większe modele domenowe, logika obiektowa, aplikacje z dziedziczeniem |
| Funkcja fabrykująca | brak new, łatwe ukrycie stanu przez closure, prostsze warianty tworzenia |
brak klasycznego dziedziczenia, łatwo przypadkiem duplikować funkcje | małe moduły, prosty stan, szybkie i elastyczne API |
W praktyce wydajność rzadko powinna być pierwszym argumentem. Znacznie częściej wygrywają czytelność, prostota testów i to, czy kolejne osoby w zespole od razu rozumieją sposób tworzenia obiektów. Jeśli konstruktor robi tylko tyle, że inicjalizuje stan, a reszta kodu pozostaje przejrzysta, zwykle jest to dobre rozwiązanie.
Co warto zapamiętać, zanim wrócisz do własnego kodu
Konstruktor ma przygotować obiekt do życia, a nie robić za ciebie całej architektury. Jeśli widzisz new, sprawdź, co naprawdę tworzy instancję, gdzie trafiają dane startowe i czy nie ma prostszego wzorca, który będzie czytelniejszy dla zespołu.
Najbezpieczniejsza zasada jest prosta: używaj konstruktora wtedy, gdy obiekt ma własny stan, przewidywalną inicjalizację i sensowny zestaw wspólnych metod. Gdy te warunki nie są spełnione, funkcja fabrykująca często daje mniej szumu i mniej błędów. Właśnie ta decyzja najczęściej odróżnia kod, który tylko działa, od kodu, który da się rozwijać bez walki z własną strukturą.