Funkcje w JavaScript — przewodnik praktyczny Przejdź do treści
JavaScript · przewodnik praktyczny

Funkcje w JavaScript
od podstaw do wzorców produkcyjnych

Zrozum deklaracje, funkcje strzałkowe, zakres, domknięcia, this, callbacki i kod asynchroniczny. Bez skrótów myślowych — z przykładami, które można wykorzystać w realnym projekcie.

8
kluczowych tematów
12+
praktycznych przykładów
18 min
szacowany czas
Schemat działania funkcji JavaScript Argumenty wpływają do funkcji, kod jest wykonywany, a wynik jest zwracany. functions.js function calculate ( value ) { const result = value * 2 ; return result; } ARG FUNCTION RET () Parametry wejście Wartość zwracana

Najważniejsza idea

Funkcja to nazwana lub anonimowa porcja zachowania. Przyjmuje dane wejściowe, wykonuje określoną pracę i opcjonalnie zwraca wynik. Dobrze zaprojektowana funkcja ma jeden czytelny cel, przewidywalny kontrakt i nazwę, która opisuje intencję.

01

Fundament

Czym jest funkcja w JavaScript?

W JavaScript funkcje są wartościami pierwszej klasy. Możesz przypisać je do zmiennej, przekazać jako argument, zwrócić z innej funkcji albo przechować w obiekcie. Dzięki temu język naturalnie wspiera kompozycję, callbacki i programowanie funkcyjne.

Każde wywołanie tworzy własny kontekst wykonania. Parametry otrzymują wartości argumentów, deklaracje lokalne trafiają do zakresu funkcji, a instrukcja return kończy wykonanie i przekazuje wynik do miejsca wywołania.

Anatomia deklaracji funkcji JavaScript Schemat wskazuje słowo kluczowe, nazwę, parametry, ciało funkcji i instrukcję return. function greet ( name ) { return `Cześć ${name}` ; } słowo kluczowe nazwa parametry return ciało funkcji
01-function-declaration.js JavaScript
function calculateGrossPrice(netPrice, taxRate = 0.23) {  if (!Number.isFinite(netPrice) || netPrice < 0) {    throw new TypeError(      'netPrice musi być liczbą większą lub równą 0'    );  }   return Number((netPrice * (1 + taxRate)).toFixed(2));} const grossPrice = calculateGrossPrice(100);console.log(grossPrice); // 123

Walidacja znajduje się przy granicy funkcji, a zwracany wynik ma stabilny typ number.

Deklaracje funkcji są hoistowane.

Można je wywołać przed miejscem deklaracji w kodzie. Nie oznacza to jednak, że warto budować na tym nieczytelną kolejność pliku — funkcje publiczne nadal powinny być łatwe do odnalezienia.

02

Składnia i semantyka

Deklaracja, wyrażenie i funkcja strzałkowa

Trzy popularne formy mogą wyglądać podobnie, ale różnią się zachowaniem. Wybieraj je świadomie — przede wszystkim ze względu na hoisting, nazwę w stack trace oraz sposób wiązania this.

declaration

Deklaracja funkcji

function loadData() {}

Najlepsza dla głównych, nazwanych operacji modułu. Czytelna w stack trace i dostępna przed deklaracją.

expression

Wyrażenie funkcyjne

const loadData = function () {};

Funkcja jest wartością przypisaną do zmiennej. Dobrze współpracuje z warunkową konfiguracją i fabrykami.

arrow

Funkcja strzałkowa

const loadData = () => {};

Zwięzła i z leksykalnym this. Świetna dla transformacji danych; zwykle zła jako metoda obiektu.

02-array-pipeline.js JavaScript
const users = [  { name: '  Anna  ', active: true },  { name: 'Marek', active: false },  { name: 'Łukasz', active: true }]; const activeNames = users  .filter(({ active }) => active)  .map(({ name }) => name.trim())  .sort((a, b) => a.localeCompare(b, 'pl')); console.log(activeNames); // ['Anna', 'Łukasz']

Funkcje strzałkowe dobrze sprawdzają się jako małe callbacki, gdy kontekst this nie jest potrzebny.

03

Projektowanie API

Parametry, wartości domyślne i destrukturyzacja

Im więcej pozycyjnych argumentów ma funkcja, tym trudniej ją poprawnie wywołać. Przy trzech lub większej liczbie opcji zwykle lepiej przyjąć pojedynczy obiekt konfiguracyjny. Nazwy pól dokumentują wywołanie i pozwalają dodawać kolejne opcje bez łamania istniejącego kodu.

03-parameters.js JavaScript
function createInvoice({  customer,  items = [],  currency = 'PLN',  issuedAt = new Date()}) {  if (!customer?.name) {    throw new TypeError('Brak nazwy klienta');  }   const total = items.reduce(    (sum, { price, quantity = 1 }) => sum + price * quantity,    0  );   return {    customer: customer.name,    currency,    issuedAt: issuedAt.toISOString(),    total: Number(total.toFixed(2))  };}

Destrukturyzowany obiekt wejściowy tworzy czytelny kontrakt i ułatwia rozbudowę funkcji.

01

Waliduj na wejściu

Błąd zgłoszony blisko źródła jest prostszy do zdiagnozowania niż niepoprawny wynik kilka wywołań dalej.

02

Nie mutuj argumentów

Twórz nowe tablice i obiekty, chyba że kontrakt jawnie mówi o mutacji i przynosi ona mierzalną korzyść.

03

Stabilizuj typ wyniku

Funkcja zwracająca raz obiekt, raz false, a raz null przerzuca złożoność na każdego klienta.

04

Kompozycja

Callbacki i funkcje wyższego rzędu

Funkcja wyższego rzędu przyjmuje inną funkcję albo ją zwraca. Ten mechanizm stoi za metodami map, filter, obsługą zdarzeń, middleware i wieloma bibliotekami reaktywnymi.

Najlepsza abstrakcja ukrywa mechanizm, ale pozostawia intencję widoczną w miejscu użycia.

04-higher-order-pipe.js JavaScript
const pipe = (...functions) => input =>  functions.reduce((value, fn) => fn(value), input); const sanitizeUsername = pipe(  value => value.trim(),  value => value.toLowerCase(),  value => value.replace(/\s+/g, '-'),  value => value.replace(/[^a-z0-9-]/g, '')); console.log(sanitizeUsername('  Jan Kowalski!  '));// jan-kowalski

pipe łączy małe transformacje w jeden czytelny przepływ od lewej do prawej.

Nie abstrahuj za wcześnie.

Jeżeli funkcja wyższego rzędu jest używana tylko raz i utrudnia czytanie, zwykły kod sekwencyjny może być lepszym rozwiązaniem.

05

Pamięć zakresu

Domknięcia — funkcja pamięta swoje otoczenie

Domknięcie powstaje, gdy funkcja zachowuje dostęp do zakresu leksykalnego, w którym została utworzona. Nawet po zakończeniu funkcji zewnętrznej potrzebne zmienne pozostają osiągalne dla funkcji wewnętrznej.

Zakresy i domknięcie w JavaScript Funkcja wewnętrzna zachowuje dostęp do zmiennych funkcji zewnętrznej po jej zakończeniu. GLOBAL SCOPE createCounter() — LEXICAL SCOPE letvalue= 10; inner function / closure return++value; Zmienna pozostaje osiągalna, dopóki istnieje zwrócona funkcja.
05-closure-counter.js JavaScript
function createCounter(initialValue = 0) {  let value = initialValue;   return Object.freeze({    increment(step = 1) {      value += step;      return value;    },    decrement(step = 1) {      value -= step;      return value;    },    current() {      return value;    }  });} const counter = createCounter(10);counter.increment(2); // 12counter.current();   // 12

value jest prywatnym stanem. Nie można zmienić go bez użycia metod zwróconego obiektu.

Gdzie domknięcia są naprawdę przydatne?

01

Fabryki funkcji i konfiguracja zachowania.

02

Prywatny stan bez klasy i bez zmiennej globalnej.

03

Memoizacja, debounce, throttle i cache.

04

Obsługa zdarzeń pamiętająca kontekst.

06

Kontekst wywołania

Jak działa this i dlaczego strzałki są inne

W zwykłej funkcji wartość this zależy od sposobu wywołania, a nie od miejsca deklaracji. Metoda wywołana przez object.method() otrzyma obiekt po lewej stronie kropki. Po odłączeniu metody kontekst znika, chyba że użyjesz bind, call albo apply.

06-this-and-bind.js JavaScript
const cart = {  items: [],   add(product) {    this.items.push(product);  },   total() {    return this.items.reduce(      (sum, item) => sum + item.price * item.quantity,      0    );  }}; cart.add({ price: 49.99, quantity: 2 }); const detachedTotal = cart.total.bind(cart);console.log(detachedTotal()); // 99.98

Metody obiektu zapisano skróconą składnią. bind tworzy nową funkcję ze stałym kontekstem cart.

Użyj zwykłej metody

Gdy funkcja ma korzystać z właściwości obiektu przez this.

×
Nie używaj strzałki jako metody

Strzałka nie tworzy własnego this; przejmuje je z otaczającego zakresu.

07

Praca asynchroniczna

Funkcje async, Promise i obsługa błędów

Funkcja oznaczona jako async zawsze zwraca obietnicę. await wstrzymuje tylko jej własny przepływ, nie blokuje całego wątku JavaScript. Błędy z odrzuconych obietnic można obsłużyć przez try...catch albo przez końcowe .catch().

Przepływ funkcji asynchronicznej Wywołanie prowadzi do obietnicy oczekującej, która kończy się powodzeniem lub błędem. CALLasync fn() PENDINGawait SETTLEDPromise FULFILLEDreturn value REJECTEDthrow error
07-async-function.js JavaScript
async function fetchUser(userId, { signal } = {}) {  const safeId = encodeURIComponent(userId);  const response = await fetch(`/api/users/${safeId}`, { signal });   if (!response.ok) {    throw new Error(      `Nie udało się pobrać użytkownika: ${response.status}`    );  }   return response.json();} const controller = new AbortController(); fetchUser('42', { signal: controller.signal })  .then(user => console.log(user))  .catch(error => {    if (error.name !== 'AbortError') console.error(error);  });

AbortSignal pozwala anulować żądanie, gdy komponent znika albo użytkownik rozpoczyna nowszą operację.

Obsługuj błąd na właściwym poziomie.

Funkcja infrastrukturalna powinna dodać kontekst techniczny i rzucić błąd dalej. Warstwa interfejsu decyduje, jaki komunikat zobaczy użytkownik.

08

Kod produkcyjny

Wzorce, testowalność i wydajność

Dobra funkcja jest mała nie dlatego, że ma określoną liczbę linii, ale dlatego, że reprezentuje jeden poziom abstrakcji. Łączy minimum zależności, jawnie komunikuje efekty uboczne i daje się przetestować bez skomplikowanego środowiska.

08-memoize.js JavaScript
function memoize(  fn,  keyResolver = (...args) => JSON.stringify(args)) {  const cache = new Map();   return function memoized(...args) {    const key = keyResolver(...args);     if (cache.has(key)) {      return cache.get(key);    }     const result = fn.apply(this, args);    cache.set(key, result);    return result;  };} const fibonacci = memoize(function calculate(n) {  if (n < 2) return n;  return fibonacci(n - 1) + fibonacci(n - 2);});

Memoizacja ma sens dla kosztownych, deterministycznych obliczeń. Cache bez limitu nie jest darmowy — zużywa pamięć.

Checklist seniora

Zanim zatwierdzisz funkcję w code review

  • Nazwa opisuje intencję, a nie implementację.
  • Argumenty i typ wyniku tworzą spójny kontrakt.
  • Efekty uboczne są jawne i ograniczone.
  • Błędy mają użyteczny kontekst.
  • Nie ma ukrytej zależności od globalnego stanu.
  • Test obejmuje przypadek typowy, graniczny i błędny.
PotrzebaNajlepszy punkt wyjściaUwaga
Główna operacja modułuDeklaracja funkcjiCzytelna nazwa i stack trace
Krótka transformacjaFunkcja strzałkowaBez własnego this
Konfigurowalne zachowanieFunkcja wyższego rzęduNie ukrywaj intencji
Prywatny stanDomknięcieKontroluj czas życia pamięci
I/O i siećasync/awaitDodaj anulowanie i błędy
FAQ

Najczęstsze pytania

Funkcje JavaScript bez niejasności

Nie wybieraj składni na podstawie domniemanej mikrooptymalizacji. Różnice zależą od silnika i kontekstu, a zwykle są nieistotne wobec czytelności oraz poprawnej semantyki this.

Czas utrwalić wiedzę

Napisz jedną funkcję mniej.
Napisz jedną funkcję lepiej.

Weź fragment z własnego projektu, nazwij jego kontrakt, oddziel obliczenia od efektów ubocznych i dodaj trzy testy: typowy, graniczny oraz błędny.

Wróć na początek

O materiale

Przewodnik projektowy dla nowoczesnego JavaScript

Treść łączy podstawy języka z decyzjami, które pojawiają się podczas projektowania modułów, integracji API i code review.

Przewijanie do góry