Deklaracja i wywołanie funkcji w JavaScript, parametry i argumenty, wartości domyślne, instrukcja return, funkcja w zmiennej i funkcje strzałkowe, zasięg lokalny i globalny oraz wywołanie funkcji przyciskiem. Lekcja 11.4 kursu INF.03.
Funkcja to nazwany blok kodu, który leży w skrypcie bezczynnie i wykonuje się dopiero wtedy, gdy ktoś wywoła go po nazwie. Dzięki temu ten sam rachunek da się uruchomić w wielu miejscach strony i za każdym razem na innych danych. W części pisemnej wracają pytania z ramką kodu („która definicja funkcji jest poprawna", „jakie będzie prawidłowe wywołanie wraz z pobraniem wyniku", „co ma za zadanie przedstawiona funkcja", „jaką wartość zwróci"). W zadaniu praktycznym cały skrypt z arkusza to zwykle jedna funkcja wywoływana kliknięciem przycisku: pobiera wartość z pola, liczy i pokazuje wynik.
Deklaracja i wywołanie
Deklaracja funkcji to zapis zaczynający się słowem function (FUNKCJA), po którym stoi nazwa, nawias okrągły z parametrami i blok instrukcji w nawiasach klamrowych. Nazwa rządzi się tymi samymi regułami co nazwa zmiennej: litery, cyfry i podkreślenie, bez spacji, bez cyfry na początku i bez polskich znaków. Wielkość liter ma znaczenie, więc Suma i suma to dwie różne nazwy.
Wywołanie to nazwa funkcji z nawiasem okrągłym. Nawias jest obowiązkowy nawet wtedy, gdy funkcja niczego nie przyjmuje: sama nazwa bez nawiasu jest tylko odwołaniem do funkcji, nie poleceniem jej wykonania.
function przywitaj() {
console.log("Witaj w sklepie komputerowym");
}
przywitaj();
przywitaj();
Wynik: napis wypisany dwa razy, bo wywołania są dwa, a deklaracja sama z siebie nie wypisuje niczego.
Deklarację wolno postawić NIŻEJ niż jej wywołanie. Przeglądarka najpierw czyta cały skrypt i zapamiętuje wszystkie deklaracje function, a dopiero potem wykonuje instrukcje po kolei. Nazywa się to podnoszeniem deklaracji (ang. hoisting).
console.log(pole(3, 4));
function pole(a, b) {
return a * b;
}
Wynik: 12, mimo że w chwili wywołania funkcja jest jeszcze o dwa wiersze niżej.
Część funkcji jest gotowa i nie trzeba ich pisać. Math.pow(2, 10) podnosi do potęgi i daje 1024, Math.floor(2.8) obcina część ułamkową w dół do 2, a parseInt("12 sztuk") wyciąga z napisu liczbę całkowitą 12. Wywołuje się je tak samo, po nazwie z nawiasem, tylko definicja leży już w przeglądarce.
Parametry, argumenty i wartości domyślne
Parametr to nazwa wpisana w nawias przy deklaracji, a argument to konkretna wartość podana przy wywołaniu. Parametr zachowuje się jak zmienna utworzona na czas jednego wywołania: dostaje wartość argumentu i po zakończeniu funkcji znika. Kolejność jest wiążąca, bo dopasowanie idzie po pozycji, nie po nazwie.
function rabat(cena, procent) {
return cena - cena * procent / 100;
}
console.log(rabat(200, 10));
Wynik: 180. Wywołanie rabat(10, 200) policzyłoby coś zupełnie innego, bo dziesiątka trafiłaby do parametru cena.
Argumentów wolno podać mniej, niż jest parametrów, i wtedy brakujący parametr ma wartość undefined (niezdefiniowana). Arytmetyka na takiej wartości daje NaN, czyli wynik, który nie jest liczbą.
function rabat(cena, procent) {
return cena - cena * procent / 100;
}
console.log(rabat(200));
Wynik: NaN. Ratunkiem jest wartość domyślna: znak równości przy parametrze podstawia wartość wtedy i tylko wtedy, gdy argumentu zabrakło.
function rabat(cena, procent = 10) {
return cena - cena * procent / 100;
}
console.log(rabat(200));
console.log(rabat(200, 25));
Wynik: 180, potem 150. Taka funkcja ma dwa parametry, a mimo to wolno wywołać ją z jednym argumentem, i dokładnie o to pyta jedno z częstszych zadań z ramką.
return, czyli co funkcja oddaje
return (ZWRÓĆ) to instrukcja, która natychmiast kończy funkcję i oddaje wartość w miejsce wywołania. Wynik trzeba czymś odebrać: przypisać do zmiennej, wstawić w większe wyrażenie albo przekazać dalej. Zapis licz(a, b) = wynik; odwraca przypisanie i jest błędem składniowym, a samo licz(a, b); wywołuje funkcję i gubi to, co ona zwróciła.
Funkcja bez return też coś oddaje: wartość undefined. To najczęściej mylona para pojęć w całym dziale, bo „wypisać" i „zwrócić" znaczą co innego.
function suma(a, b) {
console.log(a + b);
}
let wynik = suma(2, 3);
console.log(wynik);
Wynik: 5, potem undefined. Funkcja pokazała sumę, ale nie oddała jej nikomu, więc zmienna wynik została pusta. Wersja z return a + b; w środku wypisałaby na końcu 5.
Pułapka: funkcja, która ma ZWRÓCIĆ wartość, kończy się instrukcją return. Funkcja, która ma WYPISAĆ wartość, używa console.log, alert albo document.write, a zwraca undefined.
Samo document.write wywołane po wczytaniu strony kasuje jej dotychczasową treść, więc na prawdziwej stronie wynik wstawia się do wskazanego elementu, a nie wypisuje wprost.
Instrukcje stojące za return nie wykonają się nigdy, bo funkcja kończy się w tym miejscu. Wykorzystuje to warunek zatrzymania w funkcji, która wywołuje samą siebie, czyli w rekurencji. Silnia liczby 5 to iloczyn liczb od 1 do 5, a każde wywołanie zdejmuje z zadania jedną liczbę i pyta o resztę.
Wynik: 120. Wywołanie silnia(5) czeka na silnia(4), to na silnia(3) i tak do jedynki, która zwraca 1 bez kolejnego wywołania. Bez tego warunku wywołania nie skończyłyby się nigdy i przeglądarka przerwałaby skrypt.
Funkcja w zmiennej i funkcje strzałkowe
Funkcja jako wyrażenie to ta sama funkcja zapisana po prawej stronie przypisania: słowo function zostaje, nazwa znika, a wywołuje się ją przez nazwę zmiennej. Po klamrze zamykającej stoi średnik, bo cały zapis jest przypisaniem.
const pole = function (a, b) {
return a * b;
};
console.log(pole(3, 4));
Wynik: 12. Zmienna const (STAŁA) trzyma tu funkcję tak samo, jak trzymałaby liczbę: funkcja jest w JavaScripcie zwykłą wartością, którą wolno przypisać i przekazać dalej.
Funkcja strzałkowa to krótszy zapis tego samego: zamiast słowa function stoi strzałka => między listą parametrów a ciałem. Ma dwie postacie. Z klamrami działa jak zwykła funkcja i potrzebuje return. Bez klamer, gdy ciało to jedno wyrażenie, wynik tego wyrażenia jest zwracany sam i słowa return się nie pisze.
Wynik: 12, potem 10.
Różnica wobec deklaracji jest jedna, ale bolesna: funkcji przypisanej do zmiennej podnoszenie nie dotyczy. Wywołanie postawione nad przypisaniem kończy się błędem, bo w tym momencie zmienna jeszcze nie ma wartości.
Zasięg zmiennych: lokalny i globalny
Zasięg zmiennej to obszar kodu, w którym wolno użyć jej nazwy. Zmienna zadeklarowana wewnątrz funkcji jest lokalna: powstaje przy wejściu do funkcji i znika przy wyjściu, więc dwie funkcje mogą trzymać własne suma bez żadnej kolizji. Zmienna zadeklarowana poza wszystkimi funkcjami jest globalna i widzą ją wszystkie funkcje na stronie.
let waluta = "zł";
function podsumuj(cena, ilosc) {
let suma = cena * ilosc;
return suma + " " + waluta;
}
console.log(podsumuj(25, 4));
Wynik: 100 zł. Funkcja sięgnęła po globalną waluta, natomiast suma istnieje tylko w jej środku: console.log(suma) dopisane pod funkcją zatrzymałoby skrypt błędem „suma is not defined".
let (NIECH) i const ograniczają zmienną do najbliższego bloku klamrowego, więc zmienna z wnętrza if albo pętli for nie wychodzi poza tę klamrę. Starsze var (ZMIENNA) bloku nie widzi i zna wyłącznie granicę funkcji.
function policz() {
if (true) {
var stara = 1;
let nowa = 2;
console.log(nowa);
}
console.log(stara);
}
policz();
Wynik: 2, potem 1. Zmienna nowa żyje tylko w bloku if, a stara po var wychodzi z bloku i jest widoczna w całej funkcji. Przeniesienie console.log(nowa) pod klamrę zatrzymałoby skrypt błędem. W kodzie pisanym dziś używa się let i const, ale var trzeba rozpoznać, bo w arkuszach i w bankach pytań stoi wszędzie.
Przypisanie do nazwy, której nigdzie nie zadeklarowano, tworzy zmienną globalną. Stąd biorą się zmienne psujące inny skrypt na tej samej stronie, więc każda zmienna dostaje let albo const w tym miejscu, w którym jest naprawdę potrzebna. Same typy i operatory opisuje lekcja o zmiennych w JavaScripcie, a warunki i pętle używane w środku funkcji lekcja o instrukcjach sterujących. Prześledzić kolejne wywołania można w przeglądarce w interpreterze JavaScriptu. Wywołanie funkcji z przycisku
Zdarzenie to sygnał od przeglądarki, że coś się stało: użytkownik kliknął, wcisnął klawisz albo skończyło się wczytywanie strony. Atrybut onclick (PRZY KLIKNIĘCIU) wpisany w znacznik przycisku niesie kod do wykonania w chwili kliknięcia, a tym kodem jest zwykle jedno wywołanie funkcji, z nawiasem i z argumentami.
<button onclick="policzKoszt()">Policz koszt</button>
<button onclick="ustawRabat(10)">Rabat 10 procent</button>
<script src="skrypt.js"></script>
Nawias jest tu tak samo obowiązkowy jak w zwykłym wywołaniu: zapis onclick="policzKoszt" nie zrobi po kliknięciu nic. Napisy podane jako argumenty biorą się w apostrofy, bo cudzysłów zamknąłby wartość atrybutu. Plik ze skryptem dołącza się znacznikiem script z atrybutem src tuż przed zamknięciem sekcji body, żeby przycisk istniał wcześniej niż kod.
Funkcja wywoływana zdarzeniem często musi pamiętać coś między kliknięciami. Licznik kliknięć stoi wtedy na zewnątrz, bo zmienna lokalna zaczynałaby od zera przy każdym wywołaniu.
let licznik = 0;
function klikniecie() {
licznik = licznik + 1;
return "Kliknięć: " + licznik;
}
console.log(klikniecie());
console.log(klikniecie());
Na egzaminie
Pytania pisemne o funkcje mają kilka stałych kształtów. Wybór poprawnej definicji: prawidłowy jest wyłącznie zapis function nazwa(argumenty) { instrukcje; }, bez słowa new i bez typu przed nazwą, bo w JavaScripcie typu funkcji się nie deklaruje. Wybór prawidłowego wywołania wraz z pobraniem wyniku: to let wynik = licz(a, b); z przypisaniem po lewej stronie, a nie licz(a, b) = wynik; ani wywołanie z dodatkowym argumentem wynik. Ramka z funkcją i pytanie, co ona ma za zadanie: sprawdź, czy w środku stoi return, czy wypisanie na stronę, bo odpowiedzi różnią się właśnie słowem „zwrócić" albo „wypisać". Ramka z wywołaniem i pytanie o zwróconą wartość: prześledź parametry po kolei, pamiętając, że pierwszy argument trafia do pierwszego parametru. Pytanie o funkcję z dwoma parametrami, o której wiadomo, że może być wywołana z jednym: taki zapis ma wartość domyślną przy drugim parametrze.
Kilka odpowiedzi myli się regularnie. Funkcja bez return zwraca undefined, a nie zero ani pusty napis. Kod za return nie wykona się nigdy. Parametr jest zmienną lokalną, więc zmiana jego wartości w środku funkcji nie zmienia zmiennej podanej przy wywołaniu. Deklarację function wolno postawić pod jej wywołaniem, ale funkcji przypisanej do zmiennej już nie. Nazwa z polskim znakiem albo pomylona wielkość liter to typowa przyczyna błędu w oknie konsoli, o który pyta osobne zadanie.
W zadaniu praktycznym polecenie brzmi zwykle: „po naciśnięciu przycisku skrypt pobiera wartość z pola, sprawdza ją i wyświetla wynik". Tłumaczy się je punkt po punkcie. „Po naciśnięciu przycisku" to onclick z wywołaniem funkcji, „pobiera wartość" i „wyświetla wynik" to praca na elementach strony, a „sprawdza" to if w środku funkcji. Cała reszta zadania mieści się w jednej funkcji: dane wchodzą parametrami albo z pola formularza, a wynik wychodzi przez return i dopiero potem trafia na stronę. Sam formularz opisuje lekcja o formularzach HTML. Ściąga
function suma(a, b) { }
deklaracja funkcji o dwóch parametrach
suma(2, 3);
wywołanie, nawias obowiązkowy także bez argumentów
let x = suma(2, 3);
wywołanie z pobraniem zwróconej wartości
return a + b;
kończy funkcję i oddaje wartość w miejsce wywołania
brak return
funkcja zwraca undefined
function rabat(c, p = 10) { }
parametr z wartością domyślną, wolno pominąć argument
const f = function (a) { };
funkcja jako wyrażenie przypisane do zmiennej
const f = (a) => { return a * 2; };
funkcja strzałkowa z klamrami i z return
const f = (a) => a * 2;
postać skrócona, zwraca wynik wyrażenia bez return
let x w funkcji
zmienna lokalna, znika po zakończeniu funkcji
let x poza funkcjami
zmienna globalna, widoczna we wszystkich funkcjach
let i const w bloku
zasięg do najbliższej klamry, var tylko do funkcji
<button onclick="licz()">
wywołanie funkcji po kliknięciu przycisku
Math.pow(2, 10)
gotowa funkcja wbudowana, tutaj potęgowanie
Pobierz ściągę PDF