Jeśli dopiero zaczynasz swoją przygodę z JavaScriptem, pewnie szybko natknąłeś się na pojęcie funkcji. I bardzo dobrze! Bo funkcje to absolutny fundament każdego języka programowania. Bez nich Twój kod byłby jednym wielkim chaosem — powtarzającym te same linijki raz za razem. Dzięki funkcjom możesz zorganizować kod, uniknąć powtórek i pisać bardziej czytelne oraz wydajne programy.
W tym artykule pokażę Ci jak definiować funkcje w JavaScript na różne sposoby, czym się one różnią, oraz jak poprawnie z nich korzystać. Zrobimy to krok po kroku, na konkretnych przykładach. Gotowy? No to zaczynajmy!
Czym w ogóle jest funkcja?
Zanim przejdziemy do definicji, warto wiedzieć, czym funkcja w ogóle jest.
Funkcja w JavaScript to fragment kodu, który wykonuje określone zadanie. Można ją wywołać (czyli uruchomić) w dowolnym momencie programu. Często funkcja przyjmuje dane wejściowe (tzw. argumenty) i zwraca jakiś wynik (wartość zwracaną).
Brzmi technicznie? Okej, zobaczmy prosty przykład:
function powitaj() {
console.log("Cześć! Miło Cię widzieć!");
}
powitaj();
Po uruchomieniu tego kodu w konsoli zobaczysz komunikat:
Cześć! Miło Cię widzieć!
To jest właśnie funkcja — niewielki, samodzielny fragment kodu, który możesz uruchamiać kiedy chcesz.
Klasyczny sposób: function declaration
Najbardziej tradycyjny (i wciąż bardzo popularny) sposób definiowania funkcji to tzw. deklaracja funkcji.
Składnia:
function nazwaFunkcji(parametr1, parametr2, ...) {
// ciało funkcji
return wynik;
}
Przykład:
function dodaj(a, b) {
return a + b;
}
console.log(dodaj(3, 5)); // 8
Proste, prawda?
Ta forma ma jedną ciekawą właściwość: hoisting.
Co to jest hoisting? (nie mylić z hostingiem)
W JavaScript wszystkie deklaracje funkcji są „podnoszone” na początek zakresu (czyli są dostępne w kodzie, nawet jeśli je wywołasz przed faktyczną deklaracją).
powiedzCzesc();
function powiedzCzesc() {
console.log("Hej!");
}
Ten kod zadziała, mimo że wywołanie funkcji jest przed jej definicją.
Dla początkujących to czasem zaskoczenie, ale to bardzo przydatna cecha!
Alternatywa: function expression
Drugim sposobem jest przypisanie funkcji do zmiennej. To tzw. wyrażenie funkcyjne.
Składnia:
const nazwa = function(parametr1, parametr2) {
return wynik;
};
Przykład:
const pomnoz = function(a, b) {
return a * b;
};
console.log(pomnoz(4, 6)); // 24
Zwróć uwagę, że tu funkcja nie ma nazwy (to tzw. funkcja anonimowa) i jest przypisana do zmiennej pomnoz.
W odróżnieniu od deklaracji funkcji — tutaj nie działa hoisting.
Jeśli spróbujesz wywołać pomnoz() przed jej zdefiniowaniem, dostaniesz błąd.
Funkcje strzałkowe (arrow functions)
Od momentu wprowadzenia ES6 (czyli nowoczesnego JavaScriptu), pojawiła się nowa, krótsza składnia: funkcje strzałkowe.
To właśnie one stały się hitem wśród programistów.
Składnia:
const nazwa = (parametr1, parametr2) => {
// ciało funkcji
return wynik;
};
A jeśli funkcja ma tylko jeden parametr i zwraca jedną wartość, można skrócić to jeszcze bardziej:
const podwoj = x => x * 2;
Tak! To cały kod.
Przykład:
const powitaj = (imie) => {
console.log(`Cześć, ${imie}!`);
};
powitaj("Kasia");
lub w wersji skróconej:
const powitaj = imie => console.log(`Cześć, ${imie}!`);
Prosto, prawda?
Różnice między function a =>
No dobra, skoro można pisać krócej, to po co używać tej starszej składni?
Otóż różnice między tymi dwoma sposobami są bardzo istotne — szczególnie w kontekście słowa kluczowego this.
W klasycznej funkcji function, słowo this wskazuje na obiekt, który wywołał funkcję.
W funkcji strzałkowej this nie tworzy się na nowo, tylko dziedziczy kontekst z miejsca, w którym funkcja została napisana.
Przykład:
const obiekt = {
imie: "Ania",
przywitaj: function() {
console.log(`Cześć, jestem ${this.imie}`);
}
};
obiekt.przywitaj(); // Cześć, jestem Ania
Ale jeśli zmienimy function na strzałkę:
const obiekt = {
imie: "Ania",
przywitaj: () => {
console.log(`Cześć, jestem ${this.imie}`);
}
};
obiekt.przywitaj(); // Cześć, jestem undefined
Dlaczego?
Bo w funkcjach strzałkowych this nie odnosi się do obiektu, tylko do kontekstu, w którym funkcja została utworzona — czyli w tym przypadku do window (w przeglądarce).
Dlatego funkcji strzałkowych nie powinno się używać jako metod obiektu.
Funkcje anonimowe i natychmiastowo wywoływane (IIFE)
Czasem chcemy stworzyć funkcję, która wykona się natychmiast po zdefiniowaniu.
Takie konstrukcje nazywamy IIFE (Immediately Invoked Function Expression).
Przykład:
(function() {
console.log("To jest IIFE — wykonało się samo!");
})();
Tak, ta funkcja wywoła się automatycznie.
To stary, ale skuteczny sposób na tworzenie „zamkniętego” kodu, który nie zaśmieca globalnego zakresu zmiennych.
Parametry domyślne w funkcjach
JavaScript pozwala nadawać wartości domyślne parametrom.
To oznacza, że jeśli nie podasz argumentu, funkcja użyje wartości zapasowej.
Przykład:
function powitaj(imie = "Nieznajomy") {
console.log(`Cześć, ${imie}!`);
}
powitaj(); // Cześć, Nieznajomy!
powitaj("Ola"); // Cześć, Ola!
Świetne, prawda? Dzięki temu kod jest bardziej odporny na błędy.
Funkcje jako argumenty (callbacki)
Jedną z najpotężniejszych cech JavaScriptu jest możliwość przekazywania funkcji jako argumentów do innych funkcji.
Przykład:
function wykonajOperacje(a, b, callback) {
return callback(a, b);
}
const dodaj = (x, y) => x + y;
const pomnoz = (x, y) => x * y;
console.log(wykonajOperacje(3, 4, dodaj)); // 7
console.log(wykonajOperacje(3, 4, pomnoz)); // 12
Tutaj callback to funkcja przekazywana jako argument.
To właśnie na tej zasadzie działa np. setTimeout czy Array.map().
Funkcje strzałkowe w praktyce — przykład z kodem
Zróbmy coś bardziej praktycznego. Załóżmy, że mamy tablicę imion i chcemy wypisać każde z nich wielkimi literami.
Kod:
const imiona = ["Ola", "Ania", "Tomek", "Marek"]; const wielkieLitery = imiona.map(imie => imie.toUpperCase()); console.log(wielkieLitery); // ["OLA", "ANIA", "TOMEK", "MAREK"]
Tutaj użyliśmy funkcji strzałkowej jako callbacka w metodzie .map() — to idealny przykład, kiedy taka funkcja ma sens.
Funkcje zagnieżdżone
Funkcje mogą być też zagnieżdżane, czyli definiowane wewnątrz innych funkcji.
Przykład:
function zewnetrzna() {
console.log("To funkcja zewnętrzna.");
function wewnetrzna() {
console.log("To funkcja wewnętrzna.");
}
wewnetrzna();
}
zewnetrzna();
Taki zabieg jest przydatny, gdy chcesz ukryć jakąś część logiki i nie udostępniać jej poza daną funkcją.
Funkcje jako wartości zwracane
Tak, w JavaScript funkcje mogą zwracać inne funkcje.
To potężna koncepcja znana z tzw. funkcji wyższego rzędu.
Przykład:
function stworzPowitanie(imie) {
return function() {
console.log(`Cześć, ${imie}!`);
};
}
const powitajAnie = stworzPowitanie("Ania");
powitajAnie(); // Cześć, Ania!
Brzmi magicznie? W rzeczywistości to bardzo praktyczny mechanizm — często używany np. w React czy Node.js.
Funkcje asynchroniczne (async / await)
Na koniec nie sposób nie wspomnieć o funkcjach asynchronicznych.
JavaScript często działa z operacjami, które trwają (np. pobieranie danych z serwera).
Zamiast blokować cały program, można użyć funkcji asynchronicznych.
Przykład:
async function pobierzDane() {
const odpowiedz = await fetch("https://jsonplaceholder.typicode.com/posts/1");
const dane = await odpowiedz.json();
console.log(dane);
}
pobierzDane();
Tutaj async oznacza, że funkcja zwraca Promise, a await pozwala nam „poczekać” na wynik operacji.
Dzięki temu kod wygląda jak synchroniczny, ale działa asynchronicznie — piękne połączenie prostoty i mocy!
Jak widzisz, definiowanie funkcji w JavaScript to temat, który ma wiele twarzy.
Możesz użyć klasycznej składni, nowoczesnych funkcji strzałkowych, a nawet tworzyć funkcje, które zwracają inne funkcje. Wszystko zależy od tego, co chcesz osiągnąć.
Oto szybkie podsumowanie:
| Typ funkcji | Składnia | Cechy |
|---|---|---|
| Deklaracja | function nazwa() {} | Wspiera hoisting, klasyczna forma |
| Wyrażenie funkcyjne | const nazwa = function() {} | Brak hoistingu, funkcja anonimowa |
| Funkcja strzałkowa | const nazwa = () => {} | Krótsza składnia, brak własnego this |
| IIFE | (function() {})() | Wykonuje się natychmiast |
| Asynchroniczna | async function nazwa() {} | Używana do pracy z Promise i await |