Funkcja forEach w JavaScript – łatwe wyjaśnienie

Funkcja tablicowa forEach w JavaScript jest stosunkowo nową funkcją wprowadzoną w standardzie EC5. Jest to jedna z wielu funkcji tablicowych, obok np. map czy filter, które pozwalają na iterowanie po elementach tablicy bez używania tradycyjnej pętli. Funkcje te są bardzo wygodne w użyciu i pozwalają na tworzenie czystego i czytelnego kodu.

Tak samo, jak w przypadku pozostałych wymienionych funkcji, użycie forEach opiera się na wykorzystaniu funkcji callback i może być nieco trudniejsze w zrozumieniu niż tradycyjne pętle. Jeśli jeszcze nie wiesz dokładnie czym są callbacki w JavaScript to na początek zajrzyj koniecznie do tego artykułu. Omawiamy w nim dokładnie na czym polega callback i jak go wykorzystywać.

W tym artykule omówimy krok po kroku, jak wykorzystać callback z tablicą i zbudujemy własną wersję funkcji forEach. Dzięki temu zrozumiesz bez problemu jak działa ta funkcja, a korzystanie z niej stanie się dla Ciebie proste i zrozumiałe.

Zacznijmy od tradycyjnej pętli i zobaczmy, jak możemy ją przekształcić w funkcję podobną do forEach.

Użycie callback z tablicą​

Załóżmy, że z serwera pobraliśmy dane pogodowe dla ubiegłego miesiąca. Dane otrzymujemy w postaci tablicy z wartościami średniej dziennej temperatury. Zakładamy, że tablicę z danymi z serwera zapisaliśmy w stałej o nazwie temperatures.

				
					const temperatures = [3, 1, -3, -6, -1, 0, -5, -2, -1, -3, -1, 0,...]
				
			

W naszej aplikacji chcemy wyświetlić liczbę dni, w których temperatura była ujemna. Na przykład:

callback_img_8

Aby uzyskać liczbę dni spełniającą ten warunek, stworzymy licznik o nazwie zmiennej counter oraz zwykłą pętlę for. Wewnątrz pętli sprawdzimy każdy element tablicy czy spełnia ten warunek i jeśli tak, to zwiększymy licznik o 1. Nasz kod mógłby wyglądać następująco:

				
					let counter = 0;

for (let i = 0; i < temperatures.length; i++) {
    if (temperatures[i] < 0) {
        counter++;
    }
}
				
			

Po przejściu pętli zmienna counter będzie zawierała liczbę elementów tablicy, które spełniły nasz warunek. Funkcji wyświetlającej te dane w html nie będziemy już implementować, bo nie jest to dla nas teraz istotne.

Aby nasz kod stał się jeszcze bardziej czytelny, umieśćmy całą część z warunkiem if w osobnej funkcji. Następnie funkcję tę wywołamy wewnątrz pętli for z każdym elementem tablicy.

				
					let counter = 0;

function checkZero(temperature) {
    if (temperature < 0) {
        counter++;
    }
}

for (let i = 0; i < temperatures.length; i++) {
    checkZero(temperatures[i]);
}
				
			

Pętla for wywołuje teraz naszą funkcję checkZero z każdym elementem tablicy.

Wyjaśnienie, jak działa przekazanie elementów tablicy do funkcji w JavaScript

Gdybyśmy chcieli teraz sprawdzić jeszcze inny warunek dla naszej tablicy temperatures to musielibyśmy napisać pętlę for jeszcze raz  i wywołać w jej wnętrzu inną funkcję. Cała rama pętli for () byłaby identyczna, zmieniłaby się tylko funkcja, którą wywołujemy wewnątrz niej.

				
					for (let i = 0; i < temperatures.length; i++) {
    ...
    }
}
				
			

Spróbujmy więc napisać funkcję pomocniczą, która oszczędziłaby nam pisania dwa razy tego samego.

				
					function loopThrough() {
    for (let i = 0; i < temperatures.length; i++) {
        checkZero(temperatures[i])
    }
} 
				
			

Nasza funkcja loopThrough wywołuje pętlę for z tablicą temperatures. W każdej iteracji pętli wywołuje funkcję checkZero z aktualną wartością tablicy.

Na razie nie przekazujemy funkcji żadnych argumentów. Po porostu za każdym razem, kiedy wywołamy funkcję loopThrough, przeiteruje ona przez tablicę temperatures i będzie wywoływać funkcję checkZero.

Wyjaśnienie jak działa zagnieżdżanie funkcji przy iteracji w JavaScript

Zmodyfikujmy teraz nasz kod tak, aby zamiast zawsze wywoływać checkZero, funkcja loopThrough wywoływała funkcję, która będzie jej podana jako argument, czyli callback. Nazwijmy nasz callback my_function.

				
					function loopThrough(my_function) {
    for (let i = 0; i < temperatures.length; i++) {
        my_function(temperatures[i])
    }
} 
				
			

Teraz, wywołując loopThrough podamy jako argument funkcję, którą chcemy, aby była wywołana z każdą wartością tablicy wewnątrz pętli for.

				
					loopThrough(checkZero);
				
			

Aktualną sytuację możemy zilustrować następująco:

Wyjaśnienie jak działa callback JavaScript w tablicy

Cały nasz kod wygląda teraz tak:

				
					let counter = 0;

function checkZero(temperature) {
    if (temperature < 0) {
        counter++;
    }
}

function loopThrough(my_function) {
    for (let i = 0; i < temperatures.length; i++) {
        my_function(temperatures[i])
    }
}

// Wywołanie funkcji loopThrough
loopThrough(checkZero);
				
			

Przekazanie indeksu tablicy

Załóżmy, że potrzebujemy informacji nie tylko o wartości elementu tablicy, ale również o indeksie tej tablicy, z którym związana jest dana wartość. Do wywoływanej jako callback funkcji my_function możemy przekazać argumenty jakie tylko chcemy. Przekażmy więc obok temperatures[i] również bieżący indeks tablicy.

				
					let counter = 0;

function checkZero(temperature) {
    if (temperature < 0) {
        counter++;
    }
}

function loopThrough(my_function) {
    for (let i = 0; i < temperatures.length; i++) {
        my_function(temperatures[i], i)
    }
}

// Wywołanie funkcji loopThrough
loopThrough(checkZero);
				
			

Jeśli jednak spojrzymy na naszą funkcję checkZero to przyjmuje ona tylko jeden parametr, który nazwaliśmy temperature. Funkcja loopThrough wywołuje jednak teraz nasz callback checkZero z dwoma argumentami w następujący sposób:

				
					...
checkZero(temperatures[i], i);
				
			

Czy wystąpi teraz jakiś błąd? Nie. W języku JavaScript argumenty nadmiarowe, czyli takie, których dana funkcja nie przyjmuje, zostaną pominięte i nic się nie stanie. W dalszym ciągu nasz kod będzie działał poprawnie. Po prostu przekazywany przez funkcję loopThrough indeks będzie pomijany w funkcji checkZero.

Teraz, jeżeli będziemy tego potrzebować, do funkcji callback możemy dodać drugi parametr. Wartością, jaka będzie do niego przekazywana, będzie właśnie aktualny indeks tablicy. Zobaczmy przykład:

				
					let counter = 0;

function checkZero(temperature, index) {
    if (temperature < 0) {
        counter++;
        // mamy też już dostęp do aktualnego indeksu jeśli potrzebujemy
    }
}

function loopThrough(my_function) {
    for (let i = 0; i < temperatures.length; i++) {
        my_function(temperatures[i], i);
    }
}

// Wywołanie funkcji loopThrough
loopThrough(checkZero);
				
			

Warto pamiętać, że jeśli nie potrzebujemy indeksu w naszym callbacku, to nie musimy wypisywać przy implementacji funkcji wszystkich parametrów, które są do tego callbacku przekazywane. Jeśli potrzebujemy tylko pierwszego, to możemy resztę pominąć.

Bardziej uniwersalna loopThrough

Wróćmy jeszcze do samej funkcji loopThrough. Aktualnie zawsze wywołuje ona pętlę z tablicą temperatures. Iterowanie po tablicach jest tak częste w codziennej pracy programistycznej, że warto byłoby uczynić tę funkcję jeszcze bardziej uniwersalną. Idealnie byłoby, aby mogła ona iterować po różnych tablicach, nie tylko temperatures.

Na szczęście zadanie to możemy dość łatwo wykonać. Zamiast zawsze wywoływać pętlę z tablicą temperatures, możemy naszą tablicę zwyczajnie przekazać do funkcji. Nasza funkcja loopThrough będzie teraz przyjmowała w parametrze nie tylko funkcję do wywołania wewnątrz pętli, ale również tablicę, na której taką pętlę ma wywołać.

Zobaczmy jak teraz będzie wyglądać funkcja loopThrough:

				
					function loopThrough(my_array, my_function) {
    for (let i = 0; i < my_array.length; i++) {
        my_function(my_array[i], i);
    }
}
				
			

Możemy teraz przekazać do funkcji loopThrough zarówno tablicę, na której ma operować, jak i funkcję, którą ma wywołać wewnątrz pętli.

Końcowe wyjaśnienie jak działa callback w JavaScript z tablicą

Cały nasz kod będzie wyglądał teraz następująco:

				
					let counter = 0;

function checkZero(temperature, indeks) {
    if (temperature < 0) {
        counter++;
        // mamy dostęp do aktualnego indeksu jeśli potrzebujemy
    }
}

function loopThrough(my_array, my_function) {
    for (let i = 0; i < my_array.length; i++) {
        my_function(my_array[i], i);
    }
}

// Wywołanie funkcji loopThrough
loopThrough(temperatures, checkZero);
				
			

Wbudowana funkcja forEach

Omawiany przez nas przykład iterowania przez wszystkie elementy tablicy i wykonywania jakichś czynności na każdym elemencie tej tablicy jest na tyle częsty w codziennym programowaniu, że do prototypu obiektu Array dodano w języku JavaScript funkcję forEach. Funkcja ta robi coś bardzo podobnego jak nasza loopThrough. Iteruje ona przez wszystkie elementy tablicy i przy każdej iteracji wywołuje podaną przez nas funkcję, czyli callback, z wartością tablicy i aktualnym indeksem.

Zobaczmy uproszczoną definicję funkcji forEach. Pełną definicję oraz przykłady możesz znaleźć pod tym linkiem

				
					arr.forEach(callback);
				
			

Zasada działania jest podobna do naszej funkcji loopThrough. Funkcja callback będzie wykonana na każdym elemencie naszej tablicy.

Użyjmy funkcji forEach z naszym callbackiem checkZero oraz tablicą temperatures.

				
					temperatures.forEach(checkZero);
				
			

Wynik działania powyższego kodu będzie dokładnie taki sam, jak wtedy, gdy użyliśmy naszej autorskiej funkcji loopThrough.

Nasz kod będzie teraz wyglądał następująco:

				
					let counter = 0;

function checkZero(temperature, indeks) {
    if (temperature < 0) {
        counter++;
        /** indeks dodajemy w parametrze tylko wtedy,
        kiedy go potrzebujemy **/
    }
}

// Wywołanie funkcji forEach
temperatures.forEach(checkZero);
				
			

ForEach dodatkowo wywołuje również naszą funkcję z trzecim argumentem – całą tablicą, na której operuje. Jednak jak już omawialiśmy wcześniej, nie musimy w naszym callbacku przyjmować wszystkich tych parametrów. W większości przypadków wystarczy nam wartość danego elementu oraz jego indeks. 

Definiowanie funkcji wewnątrz forEach

Aby używać callbacków, nie musimy osobno najpierw definiować funkcji, a później przekazywać jej nazwy jako argument. Możemy po prostu zdefiniować ją bezpośrednio jako callback.

Poniżej przykład naszej funkcji checkZero zdefiniowanej bezpośrednio jako callback wewnątrz forEach.

				
					let counter = 0;

temperatures.forEach(function checkZero(temperature) {
    if (temperature < 0) {
        counter++;
        /* Pomineliśmy indeks w checkZero bo go nie potrzebujemy */
    }
});
				
			

W tym momencie do funkcji checkZero nie mamy już dostępu poza funkcją forEach. Jeśli jednak nie będziemy checkZero nigdzie wywoływać, to możemy również zupełnie pominąć jej nazwę i zdefiniować nasz callback jako funkcję anonimową.

Poniższy kod jest dokładnie taki sam, jak powyżej z tą różnicą, że funkcja checkZero straciła już swoją nazwę i jest funkcją anonimową.

				
					let counter = 0;

temperatures.forEach(function(temperature) {
    if (temperature < 0) {
        counter++;
        /* Pomineliśmy indeks bo go nie potrzebujemy */
    }
});
				
			

Callback i arrow functions

Być może spotkałaś lub spotkałeś się również z nieco innym zapisem. Powyższą funkcję anonimową zdefiniowaną bezpośrednio jako callback możemy również zapisać jako funkcję strzałkową. Będzie ona wyglądała następująco:

				
					let counter = 0;

temperatures.forEach((temperature) => {
    if (temperature < 0) {
        counter++;
    }
});
				
			

Funkcje strzałkowe różnią się nieco od funkcji tradycyjnych, jednak w omawianym przez nas przypadku te różnice nie mają znaczenia i możemy traktować powyższe dwa zapisy równoważnie. Jest to wciąż funkcja anonimowa, jednak nieco inaczej zapisana.

Funkcję callback możemy więc zdefiniować wcześniej i przekazać ją poprzez jej nazwę, lub bezpośrednio zdefiniować ją jako argument callback – jako zwykłą funkcję lub funkcję strzałkową.

Inne funkcje tablicowe wykorzystujące callback

W języku JavaScript całkiem sporo wbudowanych funkcji  tablicowych, które wykorzystują callback. Zasada działania takich funkcji jak np. map(), filter(), every(), some() i wielu innych jest dość podobna do omawianej przez nas funkcji forEach z tym wyjątkiem, że zwracają one jakąś wartość np. nową tablicę.

Podsumowanie

Przeszliśmy przez całą drogę budowania forEach w JavaScript , zaczynając od zwykłej pętli for i kończąc na własnej wersji takiej funkcji. Mam nadzieję, że funkcja ta nie ma już dla Ciebie żadnych tajemnic i dokładnie wiesz, o co w niej chodzi.

Jeśli artykuł ten uważasz za wartościowy to zapraszam Cię również do subskrybcji newslettera. Dzięki temu dostaniesz informacje o nowych wpisach, jak tylko pojawią się one na blogu.

Wartościowe materiały prosto na Twoją skrzynkę mailową

Zapisz się do newslettera i nie przegap kolejnych artykułów, które pogłębią Twoją wiedzę o Java Script. Absolutne zero spamu.

Uzupełniając powyższe pole wyrażam zgodę na zapis do newslettera, aby otrzymać więcej materiałów i ciekawych linków z portalu javascriptbeztajemnic.pl. Zero spamu. Mogę w każdej chwili wycofać zgodę zgodnie z Polityką Prywatności.