W końcu przechodzimy do wyjaśnienia sobie funkcji w języku JavaScript 🔔! Nie chcę się powtarzać, natomiast jak się możesz domyślać, one także są podstawą do samodzielnego programowania ⚠️. Słowo "function" w języku JavaScript jakie zostanie omówione to nie tylko funkcja, lecz także typ danych!!! Także zapraszam 😄! Ostrzegam, że ten artykuł będzie ZNACZNIE większy od pozostałych artykułów o języku JavaScript, gdyż zawiera w sobie szczegóły związane z innymi tematami ⚠️.
SŁOWO KLUCZOWE "FUNCTION" W JĘZYKU JAVASCRIPT I WSZYSTKO CO MUSISZ WIEDZIEĆ O FUNKCJACH!
"Let's begin" 🧨! Zaczniemy od terminologii. Mogło Ci się wiele razy obić o uszy słowo "funkcja" albo "metoda" (choć jest różnica pomiędzy tymi słowami). Gdybyś łapał(a) się za głowę co to jest lub też jak to zdefiniować (a wiesz jak to wygląda), to podpowiem.
Funkcja to podprogram mogący zwrócić wartość określonego typu danych, który ma prawo dysponować opcjonalnymi parametrami (fachowo "parametrami formalnymi", lecz nie komplikujmy 😁).
Istnieją 2 powody, dla których warto tworzyć funkcje w kodzie źródłowym 👇:
- pozwalają na wielokrotne wykonanie tej samej serii instrukcji przy tylko jednym zdefiniowaniu,
- pozwalają segregować i separować od siebie instrukcje, aby "trzymały się" jednego tematu np. osobno rysowanie gracza i osobno sterowanie nim.
Oba punkty dotykają arcyważnych filarów w sztuce pisania dobrego kodu: reużywalności (1) i dekompozycji (2). Zapraszam do załączonego artykułu po szczegóły.
Rozumiesz już, że aby Twój kod spełniał wymagania kodu przyjemnego w czytaniu i rozumieniu, Twoim obowiązkiem jest zrozumienie zasad definiowania funkcji 🔥.
BUDOWA FUNKCJI
Najprostsza na świecie postać funkcji w JavaScript wygląda następująco 👇:
function doSomething() {
// instrukcje
}Słowo kluczowe "function" w języku JavaScript, oznacza definicję funkcji. Zaczynasz zawsze od niego, tak jak to było z pętlą "while" i instrukcją warunkową. Następnie podajesz nazwę, która będzie "identyfikatorem" dla późniejszych wywołań funkcji (o tym poniżej). Potem umieszczamy nawiasy okrągłe i ewentualne parametry (na temat parametrów, 2 nagłówki dalej), klamerki dla bloku instrukcji i wewnątrz nich wprowadzasz instrukcje jakie się mają wykonać. Zmienne, pętle, instrukcje "if", komentarze, co tylko chcesz 👍.
To było streszczenie wszystkich podstawowych informacji, a teraz pochylimy się nad każdym z aspektów i go sobie rozwiniemy 📜. A swoją drogą, teraz już wiesz czemu funkcje "console.log" i "alert" wymagają użycia nawiasów okrągłych 😄.
WYWOŁYWANIE FUNKCJI
Mając definicję funkcji, nie oznacza to, że jakakolwiek instrukcja zostanie wykonana natychmiast po uruchomieniu programu ⛔. Funkcja w języku JavaScript jak to funkcja w każdym innym języku, musi zostać "uruchomiona" do działania.
"Używanie" funkcji nazywane jest fachowo jej "wywoływaniem". Zatem, kiedy tworzymy funkcję, to ją definiujemy, a jej wykorzystywanie gdzie indziej w kodzie oznacza wywołanie.
Musisz też wiedzieć, że definicja funkcji musi występować jako "pierwsza", zanim dojdzie do wywołania ℹ️. Innymi słowy, definicja musi "stać wyżej" w wierszach kodu. To wynika z działania interpretera i jego przetwarzania kodu schodząc z góry na dół (szczegóły w osobnym artykule).
Kierując się tym samym przykładem funkcji ukazanej powyżej, tak wyglądałoby jej wywołanie 👇:
doSomething();Podajemy już WYŁĄCZNIE SAMĄ NAZWĘ razem z nawiasami okrągłymi. Nawet kiedy funkcja nie przyjmuje żadnych parametrów, to i tak je wstawiamy ✅! To jest oznaczenie dla interpretera, że tę instrukcję ma traktować jak funkcję, a nie jak zmienną!
Natomiast gdyby się okazało, że zapomnimy o nawiasach, to wbrew pozorom nie stanie się nic złego - wtedy zostawimy samą referencję do funkcji, czyli adres (więcej o tym w dalszej części) 🚨!
PARAMETRY FUNKCJI
Często potrzebna Ci będzie informacja o jakiejś wartości zmiennej, którą trzeba pobrać z innego fragmentu kodu, bo na przykład została zdefiniowana po drugiej stronie kodu albo jaka prywatna dana składowa klasy. Tak czy owak, możesz nie mieć możliwości "wyjęcia na wierzch" zmiennej, a jest potrzebna do operacji w funkcji 🔍.
Właśnie temu służą parametry formalne. Są to zmienne lokalne występujące tylko w obrębie funkcji, do której należą ℹ️. Miejsce dla nich jest zawsze wewnątrz nawiasów okrągłych wstawianych po nazwie funkcji. Podczas wywoływania funkcji możemy wprowadzić potrzebne wartości pomiędzy nawiasy okrągłe i wtedy zostaną one przeniesione czy też wstawione w poszczególne "gniazda".
Załóżmy, że mamy taką funkcję przyjmującą jeden parametr 👇:
function writeText(text) {
console.log(text);
}i wywołanie, które może być następujące:
writeText("Komunikat.");To wówczas nasz łańcuch znaków zostanie "przekazany" funkcji i przeniesiony do miejsca parametru o nazwie "text", który z kolei wyląduje jako parametr wywoływanej funkcji "console.log".
PARAMETR FORMALNY, A PARAMETR AKTUALNY
Jeżeli lubisz być dokładny(-ą) w terminologii, to możesz już teraz opanować rozróżnianie parametrów formalnych od parametrów aktualnych, nazywanych powszechnie "argumentami". Różnica jest taka 👇:
- parametr formalny = parametr umieszczony wewnątrz definicji funkcji,
- parametr aktualny (argument) = parametr wstawiany w momencie wywoływania funkcji.
To jest raczej ciekawostka, natomiast możesz natrafić w swoim życiu na te terminy 👀.
(NIE)ZGODNOŚĆ PARAMETRÓW MILE WIDZIANA
Zanim przejdziesz dalej, małe uświadomienie w sprawie zgodności z listą parametrów, bo w języku JavaScript jest "wolna amerykanka" 🚨. Ktoś mógłby zadać pytanie:
"A co, jeżeli liczba podanych parametrów nie będzie zgodna z liczbą parametrów oczekiwanych?"
To jest dobre pytanie 👍! Efekt uboczny zależy od tego, czy jest ich za dużo, czy za mało 👇:
- jeżeli podanych parametrów jest za mało, to wszystkie te, które nie otrzymały wartości, będą typu "undefined",
- jeżeli podanych parametrów jest za dużo, to wszystkie "nadmiarowe" zostaną zignorowane,
- jeżeli typy parametrów nie są zgodne z oczekiwanymi, to operacje które z nich korzystają, zostaną wykonane niepoprawnie.
Zwrócę jeszcze uwagę na fakt, że żaden ze spełnionych punktów wcale nie oznacza pożegnania się z uruchomieniem programu! Program za to może się zawalić w trakcie jego wykonywania 🚨🚨🚨! Ma to związek z działaniem interpretera, który wykonuje instrukcje jedna po drugiej "na żywo". To nie jest kompilator, którego zadaniem jest analiza całego kodu źródłowego i zwrócenie nowego kodu wynikowego, jeśli żadne instrukcje nie łamią zasad składni. Wtedy wszystko musi być "tip top" 💯!
Język JavaScript mocno odszedł od zasady zgodności typów, jak również liczby parametrów i tak ma praktycznie każdy język skryptowy. W językach kompilowanych, takich jak C# czy Java, wszystko MUSI zazębiać się perfekcyjnie. O jeden parametr za mało, źle (chyba, że język wspiera wartości domyślne np. Python lub Kotlin i wprowadzimy je do funkcji). O jeden za dużo? Też źle. Nie zgadza się typ danych któregokolwiek z parametrów? Też żegnamy się z kompilacją.
W języku JavaScript jest "luz blues" 😄. Jeżeli coś jest nie tak jak powinno, to nie zabroni nam uruchomić programu, natomiast "kwiatek" może wyskoczyć nagle podczas działania i tego musisz być świadomy(-a)! Także to NIE JEST dobrze, że język zezwala na takie niespójności w wywołaniach funkcji ❌! Szukanie problemu nigdy nie należy do przyjemnych zadań.
LOKALNY ZASIĘG ZMIENNYCH
Umieszczając wszelkie zmienne wewnątrz funkcji, każda z nich ma lokalny zasięg widoczności 👁️. Klamerki określają niejako "granicę" istnienia wszystkich zamieszczonych tam danych. Zmienna zdefiniowana w bloku funkcji będzie występować tylko tam i nigdzie indziej! Dlatego też taki kod 👇:
function doSomething() {
let x = 5;
console.log(x);
}spowoduje prawidłowe wyświetlenie wartości zmiennej, lecz gdy przyjdzie nam do głowy odwołać się do niej na zewnątrz:
function doSomething() {
let x = 5;
console.log(x);
}
x = 80;musimy się liczyć z wyświetleniem niezdefiniowanego typu danych 🛑.
ZWROT WARTOŚCI PRZEZ FUNKCJĘ
Jeżeli znasz się już na jakimś języku programowania, to wiesz, że istnieje coś takiego, jak implementacja zwracania wartości danego typu. Używając słowa kluczowego "return", jesteś w stanie sprawić, żeby dana funkcja zwróciła coś na wyjściu wykorzystując podstawianie do zmiennej czy parametru innej funkcji.
W tym języku nie ma rozróżnienia na "void", "int" i wielu innych typów danych jakie to trzeba określać w nagłówku funkcji w większości innych języków wysokiego poziomu. "function" w języku JavaScript, to zawsze jest "function", bez względu na to, czy coś zwraca, czy nie ✅.
Popatrz na przykład na to 👇:
function getSquareOf(n) {
return n**2;
}Właśnie użyłem słowa "return" do zwrócenia kwadratu podanej liczby. "return" natychmiast kończy dalsze egzekwowanie funkcji, więc możesz go użyć nawet dla funkcji, które nic nie zwracają. Wtedy umieszczasz samo słowo kluczowe ze średnikiem i koniec:
function doSomething() {
console.log("To się wykona.");
return;
console.log("To się nie wykona.");
}Ponieważ "return" natychmiast kończy wykonywanie funkcji, odradzam zostawianie jakichkolwiek instrukcji po tym słowie kluczowym - wygląda to nieprofesjonalnie 👎.
KONWENCJA NAZEWNICZA
Wtrącenie odnośnie nazewnictwa. Obowiązuje wszystko to, co dotyczy zmiennych "let" i stałych "const". Co do funkcji, to najlepiej żeby "mówiła" co ona robi (np. "fire") albo co ma zwracać na wyjściu (np. "getGainedPoints"). To tylko sugestia, jednak stało się to powszechną normą w dzisiejszym programowaniu i dobrze jest tego się trzymać 👍.
ZMIENNA MOŻE WSKAZYWAĆ NA FUNKCJĘ!
Zadziwię być może do tego stopnia, że funkcję można bez żadnych problemów podstawić do zmiennej, czyli dosłownie "przypisać funkcję", o tak 👇:
const getSquareOf = function(n) {return n**2;};Taka funkcja określana jest jako funkcja "anonimowa", gdyż ona sama nie posiada nazwy ℹ️. To fakt, że zmienna posiada własną etykietę, natomiast ona tylko wskazuje na tę funkcję. Funkcja sama w sobie nie posiada nazwy!
Tylko dlaczego to JEST dopuszczalne? W wielu innych językach zostałoby to odebrane jako błąd składniowy! Tutaj jest całkowicie poprawne ✅! Dlaczego? Wynika to z dwóch rzeczy.
1. FUNKCJA JEST TYPEM DANYCH
Po pierwsze, "function" w języku JavaScript jako słowo kluczowe, to również osobny typ danych! Jak poddasz powyższą stałą małej próbie wywołując "console.log" i korzystając z "typeof" 👇:
console.log(typeof getSquareOf);to zobaczysz, że nie ściemniam i wyjdzie "function" 😉.
2. FUNKCJA JEST REFERENCJĄ
Po drugie, funkcja przechowuje w rzeczywistości adres w systemie szesnastkowym, czyli referencję w pamięci! Gdyby zajrzeć głęboko do języka C, to tam można znaleźć manewr polegający na przypisaniu nazwy funkcji do wskaźnika. Tak się robi w celu zaprogramowania "wywołania zwrotnego", czyli mechanizmu wywołania określonej funkcji pod wpływem jakiegoś zdarzenia np. wyjście z aplikacji. Nie chcę wchodzić w szczegóły, bo to zbyt daleko odbiega od sprawy.
W każdym razie przyjmij do wiadomości, że kiedy podstawiasz funkcję pod zmienną (albo stałą), to przechowuje ona nie samą jej definicję, tylko adres w pamięci i dzięki niemu może tę funkcję odnaleźć i wywołać 🔍. Potem możesz bez problemu wywoływać funkcję po wskaźniku, używając zmiennej 👇:
function getSquareOf(n) {
return n**2;
}
const sqr = getSquareOf;
console.log(sqr(5));co spowoduje taki "łańcuch przejść" - "console.log" przejdzie do "sqr", "sqr" wyszuka "getSquareOf" i na końcu zostanie wywołana funkcja, w której dojdzie do zwrotu wartości. A potem ta wartość wyląduje w parametrze funkcji "console.log" ☑️.
FUNKCJA JAKO PARAMETR INNEJ FUNKCJI
Mamy jeszcze jedno tajemne wcielenie funkcji 😱! Funkcja w języku JavaScript może być też przekazana do innej funkcji jako parametr 💡!!! Określa się to "funkcją wyższego rzędu" i chodzi tutaj o przekazywanie jako parametru samego kodu zamiast wartości! Otwiera to drogę do wykonywania różnych instrukcji co każde wywołanie tej samej funkcji przyjmującej za parametr wskaźnik do funkcji 🌟!
Za przykład można podać dzień roboczy pracownika. Jednego dnia może segregować papiery, innego dnia aktualizować krotki w bazie danych, a jeszcze następnego, odbierać telefony 📞. Sprowadzając to do postaci funkcji 👇:
function work(action) {
console.log("Mój dzień pracy");
if(typeof action === "function") {
action();
}
console.log("Koniec dnia pracy");
}uzyskujemy w ten sposób definicję z jednym parametrem potrzebującym wskaźnika do funkcji. A przykładowe wywołania:
function sortPapers() {
console.log("Sortuję papiery.");
}
function updateRecordsInDatabase() {
console.log("Aktualizuję rekordy w bazie danych.");
}
function answerPhones() {
console.log("Odbieram telefony.");
}
work(sortPapers);
work(updateRecordsInDatabase);
work(askBossForFreeDay);pozwalają na określanie ZUPEŁNIE RÓŻNYCH bloków kodu jakie wykonają się wewnątrz innej funkcji. Dozwolony jest również taki manewr 👇:
work(function() {
console.log("Projektuję stronę internetową.");
});To jest bezpośrednie osadzenie ciała funkcji do parametru i takie coś nazywamy "wyrażeniem lambda" 💥. Wtedy taka funkcja istnieje tylko dla tego wywołania, gdyż nie jest nigdzie zdefiniowana na zewnątrz ℹ️.
FUNKCJA NALEŻĄCA DO OBIEKTU
Znasz strukturę obiektów? Mam dobrą wiadomość dla miłośników programowania obiektowego 💛. Funkcja może się znaleźć w obiekcie, tym samym symulując metody klasy z języków czysto obiektowych, takich jak wszystkim znana Java 👇:
const player = {
health: 100,
attack: function() {
console.log("Gracz atakuje!");
}
};Wtedy taką funkcję określa się metodą, ponieważ "należy" do innej struktury 🏠!
FUNKCJA ZWRACAJĄCA OBIEKT
Dysponując dotychczas wyjaśnionymi zagadnieniami związanymi z funkcją, możemy zastosować składniowy trik polegający na połączeniu słowa "return" z klamerkami obiektu 👇:
function createPlayer() {
return {
health: 100,
attack: function() {
console.log("Gracz atakuje!");
}
};
}Uwaga! Klamerka otwierająca MUSI znaleźć się w tej samej linijce, co "return", aby język nie uznał tego za błąd składniowy ℹ️! Wtedy korzystając z takiej instrukcji:
const player = createPlayer();tworzymy obiekt gracza z metodą ❤️!
FUNKCJA-KONSTRUKTOR
Jest dostępny jeszcze jeden wariant 😅! Ostatnia postać funkcji jaką chcę zaprezentować, to funkcja jako konstruktor obiektu 🔨. Są takie sytuacje, w których chcielibyśmy, aby funkcja w języku JavaScript robiła za "fabryczkę" obiektów danego typu 🏭. Wtedy już nie korzystamy "z otoczki" w postaci klamerek, tylko piszemy wszystko już w samym ciele funkcji 👇:
function Player() {
this.health = 100;
this.attack = function() {
console.log("Gracz atakuje!");
}
}"this" w dużym skrócie, oznacza odniesienie się do samego siebie, więc kopia powstała w wyniku działania "funkcji-konstruktora", będzie posiadała właściwość "health" i metodę "attack".
Co do samego tworzenia obiektu, wykorzystujemy słowo kluczowe "new" do utworzenia instancji (kopii):
const player = new Player();Słowo "new" jest obowiązkowe, bo inaczej zostanie przypisany typ "undefined" i nie dojdzie do prawidłowego utworzenia obiektu ❌.
Wielka litera w nazwie funkcji jest wstawiona specjalnie, gdyż oznacza ona "semi klasę". Przyjmuje się też, że w takiej sytuacji nazwa powinna odzwierciedlać typ klasy (zwróć uwagę na różnicę pomiędzy "createPlayer", a "Player" ℹ️!). Jest to podstawowa konwencja z języka Java, aczkolwiek warto ją zastosować w tym i w każdym innym języku 👍.
Jest możliwość skorzystania z klasy "prawdziwej" przy użyciu słowa kluczowego "class" i tworzenia obiektów na jej podstawie ⏩! Odpuścimy sobie tutaj przykład, aby nie mieszać dwóch wątków naraz.
![]() |
Słowo kluczowe "function" posiada wiele twarzy. Jest typem danych, definiuje funkcje, przechowuje w rzeczywistości wskaźnik i może w parametrze oczekiwać innej funkcji!
Przytoczyłem chyba wszystkie transformacje jakie mogą przyjąć słowo "function" w języku JavaScript 😁. Koniec 😉!
