Poruszanie się po obiektach JavaScript

Zazwyczaj do danych w obiektach JavaScript dostajemy się przez kropkę czy też bracket notation. Działa to, gdy potrzebujemy dostać się do jednej konkretnej danej. Czasami jednak potrzebujemy pobrać wszystkie klucze albo też wszystkie wartości, a czasami potrzebujemy iterować po całym obiekcie. Przed nami kilka sposobów na dostęp do właściwości obiektu w hurtowy sposób.

Pobieranie kluczy obiektu przez Objec.keys

Wiele z tych metod, które będziemy omawiać to metody dostępne przez obiekt Object. Pierwszą metodą jest Object.keys:

const obj1 = {
  name: 'John',
  surname: 'Rambo',
  profession: 'soldier',
  print() {
    console.log(this.name);
  },
};
console.log(Object.keys(obj1)); // [ 'name', 'surname', 'profession', 'print ]
1
2
3
4
5
6
7
8
9

Do metody Object.keys przekazujemy obiekt, metoda zwraca listę kluczy tego obiektu, które są enumerowane i są właściwością tego konkretnego obiektu. Tworząc aplikacje w JavaScript na pewno wielokrotnie będziecie sięgać po tą metodę. Wbrew pozorom lista kluczy obiektów w logice aplikacji przydaje się stosunkowo często.

Ponieważ Object.keys zwraca listę, możemy sobie w łatwy sposób iterować po właściwościach obiektu, używając jakiejkolwiek pętli lub metody z obiektu Array:

Object.keys(obj1).forEach(e => console.log(e));
1

W ten sposób wypiszemy do konsoli kolejne klucze obiektu.

Ponieważ Object.keys zwraca nam tablicę wszystkich kluczy obiektu, możemy w prosty sposób sprawdzić, jak dużo właściwości ma obiekt:

console.log(Object.keys(obj1).length); // 4
1

Wystarczy na zwróconej tablicy przez Object.keys wywołać właściwość length.

Do tej metody możemy też przekazać zwykłą tablicę:

console.log(Object.keys(['foo', 'boo', 'bar'])) // [ '0', '1', '2' ]
1

W odpowiedzi dostajemy kolejne indeksy. Ponieważ tablice są tak naprawdę obiektami, to również mają klucze, do których przypisane są wartości.

Pobieranie wartości obiektu przez Object.values

Metoda Object.values jest bardzo podobna do metody Object.keys z tą różnicą, że zwraca wszystkie wartości obiektu:

const obj2 = {
  name: 'John',
  surname: 'Rambo',
  profession: 'soldier',
  print() {
    console.log(this.name);
  },
};
console.log(Object.values(obj2)); // ["John", "Rambo", "soldier", ƒ]
1
2
3
4
5
6
7
8
9

Tak jak wcześniej była tworzona lista tylko kluczy, tak teraz jest tworzona lista tylko wartości. Gdy w obiekcie, znajduje się metoda, JavaScript wywołuje na takiej metodzie toString() i stara się ją zaprezentować jako wartość tekstową.

Przy tej metodzie także należy pamiętać, że możemy ją używać tylko dla pól danego obiektu. Czyli nie odczyta ona pól, które pochodzą z dziedziczenia. Również metoda ta nie odczyta pól, które nie są numerowalne. W naszym przypadku metoda tego obiektu mogłaby mieć ustawioną konfigurację enumerable na false i pole to nie byłoby pobierane przez Objec.values.

Pobieranie właściwości przez Object.entires

Tym razem do dyspozycji mamy metodę, która pobiera zarówno klucze, jak i wartości z pól obiektu. Metoda ta to Object.entries:

const obj3 = {
  name: 'John',
  surname: 'Rambo',
  profession: 'soldier',
};
console.log(Object.entries(obj3)); 
1
2
3
4
5
6

Struktura, jaką zwraca ta metoda to zagnieżdżona tablica. Każda kolejna zagnieżdżona tablica reprezentuje parę klucz-wartość, czyli jedno pole obiektu:

  [
  ['name', 'John'],
  ['surname', 'Rambo'],
  ['profession', 'soldier']
  ]
1
2
3
4
5

Nie jest to zbyt wygodna struktura do pracy, ale możemy wykorzystać forEach i dostać się do każdej zagnieżdżonej struktury:

Object.entries(obj3).forEach((item) => {
  console.log(item[0]);
  console.log(item[1]);
});
1
2
3
4

W ten sposób dostaniemy się do każdej tablicy i za pomocą indeksów możemy pobrać element 0, który jest kluczem i element pierwszy, który jest wartością danego pola:

  name
  John
  surname
  Rambo
  profession
  soldier
1
2
3
4
5
6

Można więc powiedzieć, że jest to połączenie dwóch poprzednich metod Object.keys oraz Object.values. I znowu, metoda ta działa tylko na pola zdefiniowane dokładnie w tym obiekcie i tylko na te pola, które są enumerable.

Również nie mamy gwarancji, w jakiej kolejności będzie ułożona zwrócona tablica. Jeżeli zależy nam na kolejności, po otrzymaniu tablicy musimy ją jeszcze posortować.

Pętla for...in

Idealną pętlą do iterowania po obiektach jest pętla for...in. Pętla ta iteruje tylko po tych polach, które są enumerable, czyli tak samo, jak wszystkie inne pętle i metody. Natomiast jej zaletą jest to, że potrafi także odczytać pola, które są dziedziczone po innym obiekcie:

const obj4 = {
  name: 'John',
};

const obj5 = Object.create(obj4);
obj5.surname = 'Rambo';
obj5.profession = 'soldier';

for (const key in obj5) {
  console.log(obj5[key]);
}
1
2
3
4
5
6
7
8
9
10
11

Mamy tutaj przykład obiektu, który powstaje za pomocą Object.create. Potem do obiektu tego dodaję jeszcze dwie właściwości. Jest to więc obiekt, który posiada jedno pole dziedziczone i dwa pola jako własne.

Pętla przy każdym obrocie pobiera kolejny klucz obiektu. Gdy mamy klucz to możemy dostać się do właściwości obiektu i wyświetlić każdą wartość:

  Rambo
soldier
John
1
2
3

Widzimy, że metoda ta nie ma problemu z pobraniem wszystkich pól z obiektów. Również wyświetla dziedziczoną wartość z pola name.

Jeżeli nie chcemy wyświetlać wartości dziedziczonych, możemy zastosować metodę hasOwnProperty:

for (const key in obj5) {
  if (obj5.hasOwnProperty(key)) {
    console.log(obj5[key]); // Rambo ; soldier
  }
}

1
2
3
4
5
6

Metodę hasOnwProperty() wywołujemy bezpośrednio na obiekcie i za każdym razem przesyłamy do niej kolejny klucz z pętli. Metoda zwraca true jeżeli klucz należy do tego obiektu. Czyli został zdefiniowany w tym obiekcie i nie został odziedziczony. W tym przypadku pętla wyświetla już tylko dwie wartości.

Pętla for...in jest na ogół używana tylko do obiektów. Ma małą przydatność dla tablic. Nie zaleca się także modyfikować i usuwać elementów innych niż obecnie odwiedzany przez pętle element. Pętla iteruje po właściwościach obiektu w dowolnej kolejności. Dlatego nie możemy zakładać, że zmodyfikujemy pole obiektu, które będzie odwiedzone później i będziemy mogli jeszcze w czasie iteracji skorzystać ze zmodyfikowanego elementu.

Co warto zapamiętać

  • za pomocą Object.keys pobierzemy wszystkie klucze z obiektu
  • za pomocą Object.values pobierzemy wszystkie wartości z obiektu
  • za pomocą Object.entries pobierzemy zarówno klucze, jak i wartości obiektu
  • pętla for...in idealnie nadaje się do iterowania po właściwościach obiektu, również tych odziedziczonych
  • tablice to też obiekty, wiele metod pochodzących z Object działa też na tablice