Menu

for...of i for...in w JavaScript: pętle po tablicach i obiektach

Czym naprawdę różnią się for...of i for...in w JavaScript: wartości kontra klucze, tablice kontra obiekty i kiedy sięgać po którą pętlę.

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

Dwie pętle, dwa różne zadania

JavaScript ma dwie formy pętli, które wyglądają niemal identycznie, ale robią zupełnie co innego. for...of przechodzi po wartościach obiektu iterowalnego. for...in przechodzi po kluczach obiektu. Jedno słowo różnicy, kompletnie inne zachowanie.

Cała historia w jednym przykładzie:

Pierwsza pętla wypisuje apple, banana, cherry, czyli wartości. Druga wypisuje 0, 1, 2, czyli klucze, i to jako stringi. Wybierz złą pętlę, a spędzisz dziesięć minut na zastanawianiu się, skąd w tablicy wzięły się same liczby.

for...of: wartości z dowolnego obiektu iterowalnego

for...of to pętla, po którą będziesz sięgać najczęściej. Działa na wszystkim, co JavaScript uważa za iterowalne: tablicach, stringach, Map, Set, NodeList, generatorach i nie tylko.

Żadnego żonglowania indeksami, żadnego scores[i]. Prosisz o każdą wartość i dostajesz każdą wartość. Stringi też są iterowalne, więc for...of przechodzi po nich znak po znaku:

Działa to poprawnie także z większością punktów kodowych Unicode, co jest małą, ale realną przewagą nad odczytywaniem znaków stringa po numerze indeksu.

Indeks w pętli for...of

Jedyne, czego for...of nie daje bezpośrednio, to pozycja. Gdy jej potrzebujesz, połącz pętlę z entries():

names.entries() zwraca pary [index, value], a destrukturyzacja po lewej stronie rozdziela je na dwie zmienne. Zwykle wychodzi to czyściej niż powrót do klasycznego for (let i = 0; ...) tylko dlatego, że potrzebujesz i.

for...in: klucze obiektu

for...in powstała z myślą o zwykłych obiektach. Iteruje po wyliczalnych kluczach typu string:

Zauważ, że dostajesz klucz, a nie wartość. Żeby odczytać wartość, sięgasz z powrotem do obiektu przez user[key]. Każdy klucz jest stringiem, nawet jeśli wygląda jak liczba.

for...in przechodzi też w górę łańcucha prototypów, więc może wyciągnąć dziedziczone właściwości. Przy własnych literałach obiektów rzadko ma to znaczenie, ale przy iterowaniu po instancjach klasy albo obiektach z biblioteki warto się zabezpieczyć:

Object.hasOwn(user, key) pomija wszystko, co odziedziczone. W nowoczesnym kodzie większość osób w ogóle unika for...in i zamiast tego używa Object.keys, Object.values lub Object.entries. O tym jest następna sekcja.

Nowoczesny sposób na pętlę po obiekcie

Zamiast for...in połącz for...of z jednym z pomocników Object.*. Wybierasz ten, który pasuje do tego, czego potrzebujesz:

Object.entries jest szczególnie wygodne: destrukturyzacja pary [key, value] czyta się jak zwykłe zdanie. A ponieważ te metody zwracają tylko własne, wyliczalne właściwości obiektu, nie musisz się martwić dziedziczonymi śmieciami.

Nie używaj for...in na tablicach

Technicznie jest to dozwolone, ale łatwo się na tym potknąć:

Dostajesz 0, 1, 2 oraz tag. Każda właściwość, którą ktokolwiek dopisał do tablicy albo do Array.prototype przez polyfill, też się pojawi. Klucze są stringami, więc key + 1 skleja tekst zamiast dodawać. A kolejność iteracji nie we wszystkich przypadkach brzegowych musi się zgadzać z kolejnością w tablicy.

Praktyczna zasada:

  • Wartości tablicy? for...of arr.
  • Indeks i wartość tablicy? for...of arr.entries().
  • Sam indeks, do liczenia? Klasyczne for (let i = 0; i < arr.length; i++).
  • Klucze lub pary obiektu? Object.keys(obj) / Object.entries(obj) z for...of.

W skrócie: for...in to narzędzie niszowe. for...of razem z pomocnikami Object pokrywa wszystko, czego potrzebujesz.

break i continue działają w obu pętlach

Obie pętle obsługują standardowe sposoby wcześniejszego wyjścia:

continue przeskakuje do następnej iteracji, break całkowicie kończy pętlę. To główny powód, by wybierać for...of zamiast .forEach(): z callbacków forEach nie da się wyjść przez break, a z for...of owszem.

Podsumowanie obok siebie

Cztery pętle, cztery zadania, jeden model myślowy: jeśli chcesz wartości z czegoś iterowalnego, użyj for...of. Jeśli chcesz pracować z właściwościami obiektu, przejdź przez Object.keys / Object.values / Object.entries, również z for...of. for...in zostaw na rzadki przypadek, w którym naprawdę chcesz każdy wyliczalny klucz typu string obiektu, dziedziczony czy nie.

Dalej: wartości truthy i falsy

Każda pętla i każdy if w JavaScript w końcu zadaje to samo pytanie: czy ta wartość jest uznawana za prawdziwą? Odpowiedź nie zawsze jest oczywista: pusty string, zero, null i undefined zachowują się inaczej, niż można by się spodziewać. Następne w kolejce są wartości truthy i falsy.

Najczęściej zadawane pytania

Jaka jest różnica między for...of a for...in w JavaScript?

for...of przechodzi po wartościach obiektu iterowalnego, takiego jak tablica, string, Map czy Set. for...in przechodzi po kluczach (nazwach właściwości) obiektu. Dla tablicy ['a', 'b'] pętla for...of zwraca 'a' i 'b', a for...in zwraca '0' i '1' jako stringi.

Czy mogę użyć for...of na obiekcie?

Nie bezpośrednio, bo zwykłe obiekty nie są iterowalne. Użyj Object.keys(obj), Object.values(obj) lub Object.entries(obj), żeby dostać tablicę, po której przejdziesz pętlą for...of. Typowy wzorzec to for (const [key, value] of Object.entries(obj)).

Dlaczego nie warto używać for...in na tablicach?

Technicznie to działa, ale for...in przechodzi po każdej wyliczalnej właściwości, także dziedziczonej i dodanej przez kogoś do Array.prototype. Zwraca też klucze jako stringi, a kolejność kluczy liczbowych nie zawsze jest w pełni gwarantowana. Do wartości używaj for...of, a gdy potrzebujesz indeksu, klasycznej pętli for.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ