Czym warto zastąpić pętlę for w JavaScript
Początkujący programiści najczęściej chcą budować rzeczy od zera oraz mieć większą kontrolę nad swoim kodem. Pracując z tablicami i obiektami w JavaScript, niektórzy z nas mogą preferować użycie for loop do iteracji po elementach lub kluczach podczas wykonywania pewnej logiki. Robienie tego nie jest błędem - w końcu pętle były jednymi z pierwszych koncepcji, których nauczyliśmy się podczas zajęć z informatyki. Ważne jest jednak, aby pamiętać o wbudowanych funkcjach wyższego rzędu w JS, które sprawią, że nasz kod będzie o wiele bardziej elegancki, czytelny i DRY.
Czym jest funkcja wyższego rzędu?
Oto definicja:
Funkcja wyższego rzędu przyjmuje inną funkcję jako argument lub ją zwraca.
W tym artykule omówię siedem funkcji wyższego rzędu w JS, których ja najczęściej używam. W każdej sekcji spróbuję wyjaśnić, kiedy i jak należy każdej z nich używać.
1. forEach()
Metoda forEach()
przyjmuje funkcję i wykonuje ją dla każdego elementu tablicy.
Kiedy używać?
forEach()
ma raczej ogólne przeznaczenie, więc może być używane w wielu sytuacjach, również w tych, które realizują inne funkcje wyższego rzędu. Warto jednak pamiętać, że jeśli istnieje funkcja, która ma bardziej konkretne przeznaczenie, to zdecydowanie powinniśmy jej użyć, jeśli akurat możemy.
Inne funkcje omówimy szczegółowo później.
Składnia
arr.forEach(callback(currentValue [, index [, array]])[, thisArg]);
Na najbardziej podstawowym poziomie funkcja, która jest przekazywana do forEach
, musi przyjmować currentValue
, pod którym znajdą się kolejne elementy z iteracji. W poniższym przykładzie nazywa się to elem
. Pozostałe parametry opcjonalne są rozdzielone nawiasami kwadratowymi.
Przykład
const array = ['a', 'b', 'c'];
array.forEach(function(elem) {
// using template literals here
console.log(`Printing ${elem}`);
});
// Printing a
// Printing b
// Printing c
Aby pokazać, jak możemy używać parametrów opcjonalnych, spójrzmy na poniższy przykład:
const array = ['a', 'b', 'c'];
array.forEach(function(elem, index, array) {
console.log(`${elem} is at position ${index} of [${array}]`);
});
// a is at position 0 of the array [a, b, c]
Możemy też przekazać wartość this
, która zostanie użyta jako wartość this
w funkcji zwrotnej:
const array = ['a', 'b', 'c'];
array.forEach(function(elem, index, array) {
console.log(array == this); // checking for pointer equality
}, array); // pass in `this` as optional param
// true
2. reduce()
Kiedy używać?
reduce()
to niezwykle pomocna metoda, której można użyć do wydobycia pojedynczej wartości z tablicy przez wykonanie funkcji redukującej. Może się to wydawać nieco skomplikowane, ale chodzi tutaj o to, aby bieżącą wartość połączyć z poprzednią wartością zapisaną w rejestrze, uzyskując na koniec jedną wartość.
Poniższy przykład powinien wyjaśnić to trochę lepiej.
Mamy tutaj obiekt, który przechowuje wyniki egzaminu według sekcji, a my chcemy zobaczyć, jaki jest łączny wynik. reduce()
świetnie się tutaj sprawdzi, bo chcemy mieć tylko jedną wartość.
Składnia
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
Przykład
const scoresBySection = {
math: 60,
reading: 70,
writing: 87,
};
const sectionNames = Object.keys(scoresBySection); // get array
const marks = sectionNames.reduce(function(accumulator, sectionName) {
// the return value will be the accumulator for next iteration
return accumulator + scoresBySection[sectionName];
}, 0) // initial value
console.log(marks) // 217
W moim poprzednim artykule pisałem o tym, jak używać funkcji reduce()
, aby bezpiecznie uzyskać dostęp do wartości w obiekcie.
3. map()
Kiedy używać?
Gdybyśmy kiedykolwiek musieli zmodyfikować tablicę, to nadal moglibyśmy użyć funkcji forEach()
.
Składnia
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg])
Przykład
const array = [1, 2, 3];
array.forEach(function(elem, index, array) {
array[index] = elem * 2;
});
console.log(array); // [2,4,6]
Mówiliśmy jednak o tym, że jeśli mamy coś bardziej konkretnego niż forEach()
, to powinniśmy tego użyć. Jeśli chcemy zrobić dokładną transformację istniejącej tablicy, to powinniśmy użyć map()
. Przekazana funkcja jest wykonywana na każdej bieżącej wartości, a nowa tablica jest wtedy zwracana.
let array = [1,2,3];
array = array.map(function(elem) {
return elem * 2;
});
console.log(newArray); // [2,4,6]
Warto zapamiętać, że używamy deklaratora let
zamiast const
, ponieważ z map()
otrzymujemy nową tablicę. Musimy zatem ponownie przypisać nową wartość do tablicy zmiennych.
4. filter()
Kiedy używać?
Nie zawsze chcemy, aby wszystkie wartości znajdowały się w tablicy. Czasami chcemy tylko tych, które spełniają określone warunki. Możemy więc je odfiltrować z podzbioru oryginalnej tablicy. A podobnie jak map()
, filter()
zwraca nową tablicę.
Składnia
var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
Przykład
const array = [1,2,3,4,5];
const evenNumbers = array.filter(function(elem) {
// expressions that return 'true' are retained
return elem % 2 == 0;
});
5. find()
Kiedy używać?
Szukanie elementu w tablicy niekoniecznie musi oznaczać szukania igły w stogu siana. A to dlatego, że find()
zwraca pierwszy element, który spełnia dany warunek. Po znalezieniu naszego elementu jest on natychmiast zwracany.
Składnia
arr.find(callback(element[, index[, array]])[, thisArg])
Przykład
const array = [1,4,4,5];
const result = array.find(function(elem, index) {
console.log(`Index: ${index}`);
return elem === 4;
});
console.log(`Result: ${result}`);
// Index: 0
// Index: 1
// Result: 4
6. some()
Kiedy używać?
Czasami chcemy wiedzieć, czy w tablicy jest co najmniej jeden element, który spełnia dane warunki. some()
zwraca zatem wartość logiczną, która mówi nam, czy taki element istnieje.
Składnia
arr.some(callback(element[, index[, array]])[, thisArg])
Przykład
const array = [1,4,4,5];
const hasAtLeastAFour = array.some(function(elem) {
return elem === 4;
});
console.log(hasAtLeastAFour);
// true
7. every()
Kiedy używać?
Czasem chcemy też wiedzieć, czy każdy element w tablicy spełnia określony warunek. every()
zwraca zatem true
, jeśli wszystkie elementy spełniają warunek - w przeciwnym wypadku dostaniemy false
.
Składnia
arr.every(callback(element[, index[, array]])[, thisArg])
Przykład
const array = [1,4,4,5];
const allFours = array.every(function(elem) {
return elem === 4;
});
console.log(allFours);
// false
Podsumowanie
W powyższych przykładach użyłem słowa kluczowego function, ale w ES6 możemy użyć funkcji strzałkowych, aby uzyskać krótszą składnię. Oprócz zwięzłości funkcje strzałkowe nie mają własnego this
, co może być pomocne w zależności od sytuacji (np. przy użyciu zasięgu zewnętrznego).
array.forEach((elem, index) => {
...
});
Oto więc 7 funkcji wyższego rzędu, których regularnie używam. Więcej znajdziesz tutaj. Zwykle nie musisz tracić czasu na pisanie kodu od zera, ponieważ istnieją już natywne funkcje, które zrobią to za nas. Powinniśmy raczej starać się wykorzystać istniejące zasoby przed napisaniem kodu od zera.
Mam nadzieję, że ten artykuł był pomocny.
Oryginał tekstu w języku angielskim możesz przeczytać tutaj.
Masz jakieś przemyślenia? Podziel się nimi w komentarzu ?