Funkcja w programowaniu to blok kodu zaprojektowany do wykonania określonego zadania. Jest to jedno z podstawowych narzędzi, które pozwala na wielokrotne używanie tego samego kodu, co zwiększa modularność i czytelność aplikacji.
Definiowanie funkcji
1. Deklaracja funkcji (Function Declaration):
function pow(x, y) {
return Math.pow(x, y);
}
console.log(pow(2, 3)); // Wynik: 8
Charakterystyka: Funkcje zdefiniowane w ten sposób są „hoistowane”, co oznacza, że mogą być wywołane nawet przed ich zadeklarowaniem w kodzie.
Zastosowanie: Deklaracja funkcji jest idealna w sytuacjach, gdzie dostępność funkcji na poziomie globalnym lub w całym zakresie kodu jest pożądana, niezależnie od miejsca jej deklaracji w skrypcie.
2. Wyrażenie funkcyjne (Function Expression):
const pow = function(x, y) {
return Math.pow(x, y);
};
console.log(pow(2, 3)); // Wynik: 8
Charakterystyka: W przeciwieństwie do deklaracji funkcji, wyrażenia funkcyjne nie są hoistowane, co oznacza, że nie można ich wywołać przed ich zadeklarowaniem. Funkcje te mogą być anonimowe (jak w podanym przykładzie) lub nazwane, co wpływa na to, jak są one prezentowane w stosie wywołań.
Zastosowanie: Wyrażenia funkcyjne są szczególnie przydatne, gdy funkcja ma być przekazywana jako argument callbackowy lub gdy jej definicja powinna pozostać ukryta przed globalnym zakresem.
3. Funkcja strzałkowa (Arrow Function)
Wprowadzone w ECMAScript 6, funkcje strzałkowe oferują zwięzłą składnię i mają kilka unikalnych cech ułatwiających pracę z funkcjami w JavaScript.
Funkcja strzałkowa z jednym parametrem
const square = x => x * x;
console.log(square(5)); // Wynik: 25
Charakterystyka: Gdy funkcja strzałkowa przyjmuje tylko jeden argument, nawiasy ()
są opcjonalne. Znak =>
umieszczony bezpośrednio przed wyrażeniem x * x
automatycznie zwraca wynik tego wyrażenia. Brak jest tu słowa kluczowego return
, co jest typowe dla jednolinijkowych funkcji strzałkowych, które wykonują jedno konkretne wyrażenie
Funkcja strzałkowa z nawiasami {}
i wymaganym return
const add = (a, b) => {
const result = a + b;
return result;
};
console.log(add(5, 3)); // Wynik: 8
Charakterystyka: Użycie nawiasów ()
wokół parametrów jest wymagane, gdy funkcja przyjmuje więcej niż jeden argument. W ciele funkcji znajduje się więcej niż jedno wyrażenie, więc wymagane jest jawnie zwrócenie wyniku przy pomocy słowa kluczowego return
.
Funkcja strzałkowa bez parametrów
const printHello = () => console.log("Hello");
printHello(); // Wyświetla: "Hello"
Charakterystyka: Nawiasy ()
są obowiązkowe, gdy funkcja nie przyjmuje żadnych argumentów. Funkcja ta bezpośrednio wykonuje działanie, takie jak wypisanie tekstu, i nie zwraca wartości. Dzięki jednolinijkowej formie, return
jest niepotrzebny.
Specjalna cecha funkcji strzałkowych:
Funkcje strzałkowe nie tworzą własnego kontekstu this
. Oznacza to, że this
jest dziedziczone z otaczającego zakresu kodu, co jest kluczowe w przypadkach, gdy funkcja jest używana jako callback i musi odwołać się do this
zewnętrznego kontekstu.
Zastosowanie:
Funkcje strzałkowe są szczególnie użyteczne w operacjach na tablicach, takich jak map
, filter
, reduce
, oraz w każdym przypadku, gdy potrzebne jest utrzymanie kontekstu this
bez automatycznego wiązania go do nowego obiektu.
4. IIFE (Immediately Invoked Function Expression):
(function() {
console.log("Hello, world!");
})();
Definicja: IIFE to wyrażenie funkcyjne, które jest natychmiast wywoływane po jego zdefiniowaniu.
Zastosowanie: IIFE jest szczególnie przydatne w starszych wersjach JavaScript, które nie obsługują zmiennych blokowych takich jak let
czy const
. Umożliwia tworzenie nowego zakresu, izolując zmienne i logikę od globalnego zakresu, co pomaga unikać konfliktów i zanieczyszczenia globalnego środowiska.
5. Funkcje anonimowe
Funkcje anonimowe to funkcje bez jawnie przypisanej nazwy, użyteczne w wielu kontekstach programistycznych.
Zastosowanie:
Jako callback w metodach tablicy:
const numbers = [1, 2, 3];
const doubled = numbers.map(function(num) {
return num * 2;
});
console.log(doubled); // Wynik: [2, 4, 6]
Tutaj funkcja przekazana do map jest anonimowa, służy tylko do zdefiniowania operacji przekształcenia elementów tablicy.
W operacjach asynchronicznych:
setTimeout(function() {
console.log("Hello after 3 seconds");
}, 3000);
W powyższym przykładzie, funkcja przekazana do setTimeout
jest anonimowa i wywoła się po upływie określonego czasu.
W IIFE (Immediately Invoked Function Expression):
(function() {
console.log("Executed immediately");
})();
W tym przypadku, anonimowa funkcja jest wywołana natychmiast po zdefiniowaniu. Jest to częsty sposób na izolowanie zmiennej lub logiki w swoim zakresie.
W programowaniu zdarzeniowym:
document.getElementById('my-button').addEventListener('click', function() {
alert('Button clicked!');
});
Funkcja obsługi zdarzenia kliknięcia jest anonimowa i służy jedynie do reakcji na kliknięcie przycisku.
Zwracanie wartości przez funkcje
W JavaScript, funkcje bez słowa kluczowego return
domyślnie zwracają undefined
.
function sayHello(name) {
console.log(`Hello, ${name}!`);
}
const greeting = sayHello("Alice");
console.log(greeting); // undefined
Funkcja sayHello
wyświetla wiadomość, ale nie zwraca wartości, więc zmienna greeting
otrzymuje undefined
.
Jak działa hoisting z funkcjami?
Deklaracje funkcji
console.log(greet("Alice")); // Wywołanie funkcji przed jej zadeklarowaniem
function greet(name) {
return `Hello, ${name}!`;
}
W przypadku deklaracji funkcji, JavaScript „przenosi” całą definicję funkcji na górę zakresu. Dzięki temu można wywołać funkcję greet
przed jej zadeklarowaniem w kodzie.
Wyrażenie funkcyjne
console.log(greet("Alice")); // TypeError: greet is not a function
const greet = function(name) {
return `Hello, ${name}!`;
};
W przypadku wyrażeń funkcyjnych, tylko deklaracja zmiennej (const greet
) jest hoistowana na górę zakresu jako undefined
. Definicja funkcji nie jest dostępna, dopóki wykonanie kodu nie dotrze do linii, w której funkcja jest faktycznie zdefiniowana. Dlatego próba wywołania greet
przed przypisaniem do niej funkcji skutkuje błędem TypeError
.
Nazwa funkcji dostępna w stack trace
function calculateArea(width, height) {
if (typeof width !== 'number' || typeof height !== 'number') {
throw new TypeError("Both width and height must be numbers.");
}
return width * height;
}
function triggerError() {
return calculateArea("width", 10); // Celowo błędne wywołanie dla demonstracji
}
try {
triggerError();
} catch (error) {
console.error(error.stack);
}
//// Stos wywołań pokaże `calculateArea` i `triggerError`
Nazwy funkcji calculateArea
i triggerError
są widoczne w stack trace, co ułatwia debugowanie. W przypadku błędów, JavaScript dostarcza dokładne informacje o miejscu, w którym wystąpił błąd, włącznie z nazwami funkcji i liniami, co pozwala szybko zidentyfikować i rozwiązać problem.
//VM749:15 TypeError: Both width and height must be numbers.
//at calculateArea (<anonymous>:3:15)
//at triggerError (<anonymous>:9:12)
//at <anonymous>:13:5