Menu

Obiekty w JavaScript: właściwości i metody

Jak naprawdę działają obiekty w JavaScript: tworzenie, odczyt i zapis właściwości, dodawanie metod i wzorce, dzięki którym kod z obiektami jest czytelny.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Obiekt to worek nazwanych wartości

Tablica grupuje wartości według pozycji. Obiekt grupuje je według nazw. Gdy to, co modelujesz, ma opisane części (użytkownik z imieniem i wiekiem, żądanie z metodą i adresem URL), sięgnij po obiekt.

Najkrótszy sposób na utworzenie obiektu to literał obiektu:

Każdy wpis to para klucz: wartość. Pod spodem klucze są stringami (cudzysłowy są opcjonalne dla poprawnych identyfikatorów, takich jak name). Wartości mogą być wszystkim, co JavaScript potrafi reprezentować: liczbami, stringami, wartościami logicznymi, tablicami, funkcjami, a nawet innymi obiektami.

Przecinek po ostatnim wpisie jest w porządku. Większość zespołów go zostawia: dzięki temu diffy są czystsze, gdy później dodajesz nową właściwość.

Odczyt i zapis właściwości

Do właściwości można się dostać na dwa sposoby: notacją kropkową i notacją nawiasową.

Notacja kropkowa jest schludniejsza i to po nią sięgasz domyślnie. Notacja nawiasowa przydaje się, gdy klucz jest zapisany w zmiennej albo gdy nie jest poprawnym identyfikatorem:

Odczyt właściwości, która nie istnieje, zwraca undefined, bez żadnego błędu:

To może po cichu ukrywać literówki. Jeśli chcesz głośnego błędu, musisz to sprawdzić jawnie.

Dodawanie i usuwanie właściwości

Obiekty są otwarte na zmiany: klucze możesz dodawać w dowolnym momencie:

A usuwać je przez delete:

delete to jeden z tych operatorów, których nie używasz na co dzień, ale gdy musisz całkiem pozbyć się klucza (a nie tylko ustawić go na undefined), to właściwe narzędzie. Ustawienie user.email = undefined zostawia klucz: "email" in user nadal da true.

Sprawdzanie, czy właściwość istnieje

Są trzy sposoby, każdy o trochę innym znaczeniu:

  • in sprawdza, czy klucz istnieje, także wśród kluczy odziedziczonych z łańcucha prototypów.
  • Object.hasOwn(obj, key) sprawdza tylko własne klucze obiektu. Używaj go, gdy chcesz pominąć to, co odziedziczone. Zastępuje starsze wywołanie hasOwnProperty.
  • obj.key !== undefined działa w większości przypadków, ale kłamie, gdy właściwość jest jawnie ustawiona na undefined.

W razie wątpliwości wybierz Object.hasOwn: robi to, o co najpewniej ci chodzi.

Metody: funkcje, które mieszkają w obiektach

Właściwość, której wartością jest funkcja, to metoda. Wewnątrz literału obiektu jest dla nich krótka składnia:

this wewnątrz metody wskazuje na obiekt, na którym metodę wywołano, w tym przypadku user. Dzięki temu greet wie, czyjego imienia użyć.

Na jedno trzeba uważać: funkcje strzałkowe nie mają własnego this, więc to zły wybór dla metod, które muszą czytać inne właściwości obiektu:

Do wszystkiego, co korzysta z this, używaj skróconej składni metod (greet() { ... }). Funkcje strzałkowe świetnie sprawdzają się jako callbacki, ale nie jako metody obiektu.

Obiekty zagnieżdżone

Wartościami mogą też być obiekty, i to na dowolnej głębokości:

Zagnieżdżoną właściwość odczytujesz przez łańcuch: user.address.city. Haczyk: jeśli którykolwiek krok po drodze to null lub undefined, dostajesz TypeError:

console.log(user.profile.city);
// TypeError: Cannot read properties of undefined (reading 'city')

Optional chaining (user.profile?.city) to współczesne rozwiązanie: zwraca undefined zamiast rzucać błąd, gdy brakuje ogniwa. W dalszej części tego rozdziału jest mu poświęcona osobna strona.

Iterowanie po obiekcie

Gdy musisz przejść po każdym kluczu obiektu, potrzebujesz rodziny Object.keys, Object.values i Object.entries:

Object.entries jest najbardziej przydatne: daje klucz i wartość naraz i dobrze łączy się z destrukturyzacją tablic.

Jest też pętla for...in, ale przechodzi również po właściwościach odziedziczonych, a zwykle nie o to chodzi:

for (const key in scores) {
  console.log(key);  // works, but includes inherited keys
}

Wybieraj Object.keys / Object.entries, chyba że naprawdę chcesz uwzględnić właściwości odziedziczone.

Skróty składni, które warto znać

Dwa kawałki lukru składniowego, które zobaczysz wszędzie:

Skrócony zapis właściwości (property shorthand) działa, gdy nazwa zmiennej jest taka sama jak klucz. Klucze obliczane (forma [expr]) pozwalają budować nazwy właściwości dynamicznie, co przydaje się przy funkcjach aktualizujących pole po nazwie.

Równość obiektów to równość referencji

Na tym każdy potyka się przynajmniej raz:

=== na obiektach sprawdza, czy obie strony wskazują ten sam obiekt w pamięci, a nie czy mają taką samą zawartość. Dwa obiekty o identycznych właściwościach to nadal dwa różne obiekty.

Żeby sprawdzić, czy mają ten sam kształt, musisz porównać pola ręcznie albo użyć funkcji do głębokiego porównania (deep equality). JSON.stringify(a) === JSON.stringify(b) to szybki i prowizoryczny test, który działa dla zwykłych danych, ale wykłada się na funkcjach, undefined i referencjach cyklicznych.

Obiekty są mutowalne nawet z const

const przypina zmienną do jednej wartości. Nie zamraża obiektu, na który ta wartość wskazuje:

Jeśli naprawdę chcesz niemutowalności, Object.freeze(user) blokuje dalsze zmiany (płytko: zagnieżdżone obiekty nadal da się zmieniać). W praktyce większość kodu opiera się na konwencji, a nie na Object.freeze: traktuj obiekty z const jako "nie przypisuj ponownie tego wiązania", a niemutowalność rozwiązuj na poziomie projektu.

Dalej: tablice

Obiekty i tablice to dwa klocki, z których zbudowana jest cała reszta. Obiekty obsługują dane z etykietami, tablice dane uporządkowane. W następnej części: jak działają tablice w JavaScript i garść metod (push, slice, map i spółka), które wykonują większość ciężkiej pracy.

Najczęściej zadawane pytania

Jak utworzyć obiekt w JavaScript?

Najczęściej przez literał obiektu: const user = { name: 'Ada', age: 30 }. W nawiasach klamrowych są pary klucz-wartość oddzielone przecinkami. Klucze to stringi (cudzysłowy są opcjonalne dla poprawnych identyfikatorów), a wartości mogą być czymkolwiek: liczbami, stringami, tablicami, funkcjami, innymi obiektami.

Jaka jest różnica między notacją kropkową a nawiasową?

user.name i user['name'] robią to samo, gdy klucz jest znanym identyfikatorem. Nawiasów używaj, gdy klucz jest zapisany w zmiennej (user[key]), zawiera znaki, których notacja kropkowa nie rozumie (spacje, myślniki), albo zaczyna się od cyfry. W pozostałych przypadkach notacja kropkowa jest czytelniejsza.

Jak sprawdzić, czy obiekt ma daną właściwość?

'name' in user sprawdza, czy klucz istnieje, także wśród odziedziczonych. Object.hasOwn(user, 'name') sprawdza tylko własne właściwości obiektu: to współczesny zamiennik hasOwnProperty. Sprawdzenie user.name !== undefined też działa, ale kłamie, gdy właściwość jest jawnie ustawiona na undefined.

Jak iterować po obiekcie w JavaScript?

for (const key in obj) przechodzi po kluczach, także odziedziczonych. Częściej używa się Object.keys(obj), Object.values(obj) lub Object.entries(obj) z pętlą for...of albo .forEach(). Object.entries przydaje się, gdy w jednej iteracji potrzebujesz i klucza, i wartości.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ