?

Cześć!

(naciśnij spację, żeby kontynuować)

Funkcje

Grafika zapożyczona z serwisu media.quizizz.com

Funkcje

Dziś poznamy lepiej funkcje. Są one bardzo ważną częścią języka JavaScript (i programowania w ogóle).

Do czego przydają się funkcje?

Jeżeli byłyście na naszym warsztacie, pisałyście tam własne funkcje do obsługi przycisków na stronie.

Funkcje przydają się, gdy chcemy ten sam kawałek kodu wykonać kilka razy.

Można wykonywać go za każdym razem na innych danych.

Funkcja może też zwracać wynik swojego działania, na którym reszta kodu będzie działać dalej.

Więc czym to się właściwie różni od pętli?

Te z was, ktore znają pętle, wiedzą że też mogą wykonywać ten sam kod kilka razy...

Pętlę wykonujemy kilka razy jeden po drugim, i na tym koniec. Funkcję możemy wywołać w dowolnym momencie, z dowolnego miejsca w kodzie.

Pętla musi mieć z góry przygotowane dane i działa na nich w ściśle określonej kolejności (0, 1, 2, ...). Funkcja za każdym razem może dostać nowy zestaw danych, niezależny od tego co było wcześniej.

Podział na funkcje sprawia, że kod staje się bardziej przejrzysty, i łatwiej szukać w nim błędów.

A w praktyce?

Grafika zapożyczona z orsgroup.com;

A w praktyce?

Żeby wypisać karteczki w pętli, musimy z góry pamiętać, co na której będzie, w jakiej idą kolejności, i wypisać je wszystkie jedna po drugiej.

Używając funkcji możemy pojedynczo dodawać nowe karteczki, kiedy tylko nam coś wpadnie do głowy - nawet kiedy zajmujemy się właśnie czymś zupełnie innym.

Na razie bez komputerów...

Załóżmy, że chcemy dodać nową karteczkę do tablicy z poprzedniego slajdu. Zastanówmy się razem, jakie czynności będziemy musiały wykonać?

Dodawanie nowej karteczki - na sucho :-)

1. Weź samoprzylepną karteczkę.
2. Weź długopis.
3. Napisz na karteczce tekst.
4. Przyklej karteczkę na tablicy.
5. Odłóż długopis (?)

Czegoś tu brakuje?

Skąd wiemy, jaki tekst napisać na karteczce?

Dodawanie nowej karteczki - na sucho :-)

Funkcja: Dodaj nową karteczkę ( dany_tekst ):
1. Weź samoprzylepną karteczkę.
2. Weź długopis.
3. Napisz na karteczce dany_tekst.
4. Przyklej karteczkę na tablicy.
5. Odłóż długopis (?)

Wszystko jasne do tej pory?

Anatomia funkcji

Funkcję w JavaScript deklarujemy przy użyciu słowa function:

function sum(x, y) {
  return x + y;
}

Wartości w nawiasie to są parametry - dane, na których funkcja operuje.

Słowem return oznaczamy wartość zwracaną przez funkcję, na której możemy operować dalej:

let result = sum(2, 3) // result = 5

Gotowe? Do dzieła

Pliki z kodem do pobrania znajdziecie tutaj.

Otwórzmy teraz plik exercise.html

Zadanie 1

W pliku mamy tablicę korkową z dodaną na niej karteczką.

Naszym zadaniem będzie napisać funkcję, która doda do tablicy nową karteczkę, z dowolnym, podanym przez nas tekstem.

Zadanie 1 - funkcje pomocnicze

W tym celu możemy wykorzystać dwie napisane już funkcje pomocnicze:

  • getNote() - która tworzy nową karteczkę i zwraca ją jako wynik działania,
  • addNoteToBoard( note ) - bierze karteczkę przekazaną jako parametr i dodaje ją do tablicy.

Coś jeszcze?

Zastanawiacie się pewnie, w jaki sposób napiszemy tekst na karteczce?

Nasza karteczka, jak każdy element dokumentu HTML ma właściwość innerText. Cokolwiek w niej zapiszemy, stanie się treścią karteczki, np:

element.innerText = 'Kodowanie jest fajne!'

Do dzieła!

Napiszmy funkcję createNewNote, która przyjmie jako parametr tekst, pobierze nową karteczkę korzystając z funkcji getNote(), napisze tekst na karteczce - zapisując go w innerText, a następnie doda karteczkę do tablicy przy użyciu funkcji addNoteToBoard( note ).

Gotowe?

Powinno nam wyjść coś takiego:

function createNewNote( text ) {
    let newNote = getNote();
    newNote.innerText = text;
    addNoteToBoard( newNote );
}

Wszystko się zgadza?

Przetestujmy teraz tę funkcję w konsoli.

Jak to zrobimy?

createNewNote( "Zaprosić koleżankę na następne warsztaty" );

Bonus: spróbuj kliknąć na długopis i zobacz, co się wtedy stanie.

Jeżeli twoja funkcja nazywa się createNewNote i działa jak trzeba, powinnaś mieć wygodną opcję dodawania nowych karteczek.

A co jeśli nie podamy żadnego tekstu?

Żeby karteczka nie była pusta, można zaproponować domyślny tekst, który pojawi się tam, kiedy nie przekażemy żadnego parametru.

W tym celu możemy użyć parametrów domyślnych.

Jak działają parametry domyślne?

Przy deklarowaniu funkcji i jej parametrów możemy podać domyślną wartość po znaku =. Na przykład:

function setColor( color = 'yellow' ) {
    // jeżeli podasz kolor w nawiasie, funkcja użyje tego, który podałaś
    // jeżeli nie podasz żadnego, automatycznie ustawi się żółty
}

Wykorzystajmy to w praktyce.

W funkcji createNewNote, którą wcześniej napisałaś, dodaj domyślną treść, która ma się pojawić, kiedy nie podamy żadnego tekstu.

Proste, prawda?

function createNewNote( text = "Dokupić więcej żółtych karteczek" ) {
    let newNote = getNote();
    newNote.innerText = text;
    addNoteToBoard( newNote );
}

No to lecimy dalej :-)

Funkcja jako parametr innej funkcji

W JavaScripcie parametrem funkcji może być praktycznie wszystko - włącznie z inną funkcją.

Przydaje się to na przykład, kiedy chcemy powiązać funkcję z jakimś zdarzeniem, na przykład naciśnięciem klawisza.

Albo zapamiętać zadanie do zrobienia na potem, zupełnie jak na żółtej karteczce.

Przekazywanie funkcji jako parametr

Żeby przekazać funkcję jako parametr podajemy w nawiasach jej nazwę:

function cleanUpSomething () {
  // funkcja ktorą chcemy wykonać za minutę
}

setTimeout( cleanUpSomething, 60000 );
// wykonaj funkcję cleanUpSomething za 60 sekund

Przekazywanie funkcji jako parametr

Możemy też zapisać całą funkcję jako parametr innej funkcji:

setTimeout( function cleanUpSomething () {
  // funkcja ktorą chcemy wykonać za minutę
}, 60000 );
// wykonaj funkcję cleanUpSomething za 60 sekund

Przekazywanie funkcji jako parametr

To czego nie możemy, to podać w nawiasach nazwę funkcji wraz z nawiasami:

function cleanUpSomething ( data = '' ) {
  // funkcja ktorą chcemy wykonać za minutę
}

setTimeout( cleanUpSomething(), 60000 );
setTimeout( cleanUpSomething( someData ), 60000 );
// w obu przypadkach funkcja cleanUpSomething wykona nam się od razu

Jeżeli tak zrobimy, funkcja wykona nam się na miejscu zamiast być przekazana dalej.

Do czego nam to potrzebne?

Otwórzmy teraz plik z drugim zadaniem, exercise2.html

Zadanie 2

Mamy przed sobą częściowo oprogramowany kalkulator. W tym momencie jeszcze żaden guziczek nie działa - żeby go ożywić, musimy napisać kilka nowych funkcji, i podłączyć je pod odpowiednie guziki.

Czyszczenie ekranu

Zacznijmy od w miarę prostego przypadku. Przycisk C zazwyczaj służy do wyczyszczenia ekranu - zmiany tego, co na nim jest na zero. Zastanówmy się (na razie po polsku) co powinno się wydarzyć, kiedy ktoś wciśnie ten przycisk.

Gotowe?

Zobaczmy, co nam wyszło:

Kiedy wciśnięto przycisk C:
1. Zapisz 0 jako aktualny numer,
2. Wyświetl aktualny numer na ekranie.

Do wyświetlenia liczby na ekranie możemy użyć właściwości innerText, którą poznałyśmy już wcześniej. Tylko jak podpiąć ją do ekranu kalkulatora?

Dowolny element na stronie możemy "zbadać" narzędziami przeglądarki:

A następnie pobrać jego selektor w drzewie po prawej:

Używając tego selektora możemy teraz dobrać się do elementu, używając document.querySelector, np:

document.querySelector('#screen').innerText = "Siemanko!";

Możemy uzupełnić nasz pseudokod o bardziej szczegółowe instrukcje:

Kiedy naciśnięto przycisk C:
1. Zapisz 0 jako aktualny numer,
2. Znajdź element o selektorze '#screen',
3. Podmień jego zawartość na aktualny numer.

Wszystko jasne?

W pliku exercise2.html dodaj nową funkcję o nazwie reset. Funkcja ta powinna zrobić dwie rzeczy: zapisać 0 w zmiennej currentNumber, oraz złapać element '#screen' i podmienić jego zawartość, na to co mamy w tej zmiennej. Pamiętaj, żeby użyć tych samych nazw funkcji i zmiennych, inaczej nasz kod nie będzie współpracował z resztą.

I jak nam poszło?

function reset() {
    currentNumber = 0;
    document.querySelector('#screen').innerText = currentNumber;
}

Przetestujmy, jak to działa w konsoli.

To, czego potrzebujemy teraz to powiązać tę funkcję z akcją, kiedy ktoś naciśnie na przycisk C

Uwaga, będzie dużo nowych rzeczy naraz!

Są tu absolwentki warsztatów Girls.js?

Poznałyśmy tam metodę addEventListener, która pozwala powiązać funkcję z wydarzeniem na elemencie.

  • element.addEventListener( wydarzenie, akcja-do-wykonania ) - wykonuje podaną akcję, gdy z elementem stanie się coś konkretnego.

addEventListener - przykład

document.querySelector( '#button' ).addEventListener( 'click',
nazwaMojejFunkcji );

Czyli jak to wszystko razem będzie po polsku?

Dopiszmy brakujące fragmenty pseudokodu:

Funkcja reset:
    1. Zapisz 0 jako aktualny numer,
    2. Znajdź element o selektorze '#screen',
    3. Podmień jego zawartość na aktualny numer.

1. Znajdź element o selektorze '#C',
2. Powiąż wydarzenie 'click' na tym elemencie z funkcją reset.

No to do dzieła!

Powiąż teraz funkcję reset, którą napisałaś, z przyciskiem C. Do przycisku możesz odnieść się przy użyciu document.querySelector. Jeżeli nie jesteś pewna jaki jest selektor elementu, sprawdź w narzędziach przeglądarki. Użyj addEventListener żeby powiązać kliknięcie na tym przycisku z funkcją reset.

Gotowe?

Tak powinna wyglądać całość:

function reset() {
    currentNumber = 0;
    document.querySelector('#screen').innerText = currentNumber;
}

document.querySelector('#C').addEventListener( 'click', reset );

Sprawdźmy, jak to teraz działa.

Przyciski z liczbami

Teraz zajmiemy się oprogramowaniem przycisków od 0 do 9.

Nie będziemy szczegółowo wszystkiego same programować, bo dzieje się tu sporo:

  • Musimy wiedzieć, czy dopisujemy cyfry do kolejnej liczby, czy zaczynamy nową,
  • Uważamy, żeby nie dopisać cyfr do zera z przodu (bo wyszłoby nam 05),
  • Uważamy, żeby nie przekroczyć 11 znaków, ktore mieszczą się na wyświetlaczu,
  • Zapamiętujemy całą liczbę jako tekst, żeby łatwiej nam było wprowadzać ułamki.

Tym wszystkim zajmuje się gotowa już funkcja numberKeyPressed( number ), zapisana w pliku calc.js (w domu możecie sprawdzić, jak ta funkcja działa).

Bierze ona liczbę od 0 do 9 jako parametr, i robi wszystko co potrzeba, żeby w odpowiedni sposob dopisać ją na ekranie.

Naszym zadaniem będzie powiązać funkcję numberKeyPressed z naciśnięciem kolejnych guziczków. Przykładowo, po wciśnięciu guziczka 1 chcemy wywołać tę funkcję z parametrem 1.

Tradycyjnie, zapiszmy najpierw po polsku, co chcemy, żeby się stało.

Gotowe?

1. Znajdź przycisk z numerem 1 (selektor '#number1')
2. Powiąż wydarzenie 'click' na tym elemencie z funkcją numberKeyPressed.
3. Przekaż do tej funkcji parametr 1.

Jest tylko jeden haczyk...

Nie możemy tak po prostu przekazać jako parametr funkcji numberKeyPressed z własnym parametrem.

Jeżeli zapiszemy to jako numberKeyPressed( 1 ), funkcja wykona nam się od razu, zamiast powiązać z przyciskiem.

Co z tym zrobimy? Na ratunek przyjdzie nam funkcja anonimowa.

Funkcja anonimowa

To funkcja, która nie ma swojej nazwy. Wygląda na przykład tak:

function (x, y) {
  return x + y;
}

Po co więc nam funkcje anonimowe?

Możemy je przypisać do zmiennej:

let sum = function (x, y) {
  return x + y;
}

Ale to wiele nie różni się od funkcji nazwanej:

sum( 2, 3 ) // zwróci 5

Funkcja anonimowa

Prawdziwy użytek można zrobić z funkcji anonimowych przy przekazywaniu jej jako parametr do innej funkcji, albo przy tworzeniu ich dynamicznie (czytaj: na bieżąco).

Pamiętacie nasz przykład z odkładaniem zadania na potem?

function cleanUpSomething ( data = '' ) {
  // funkcja ktorą chcemy wykonać za minutę
}

setTimeout( cleanUpSomething( someData ), 60000 );
// funkcja cleanUpSomething wykona nam się od razu

setTimeout( function () {
    cleanUpSomething( someData );
}, 60000 );
// funkcja cleanUpSomething wykona się za minutę,
// i będzie miała dostęp do zmiennej someData

Wszystko w miarę jasne?

To spróbujmy to wykorzystać w praktyce :-)

Napisz kod, który powiąże przycisk 1 z wywołaniem funkcji numberKeyPressed z odpowiednim parametrem. Potrzebna ci będzie funkcja anonimowa, żeby móc jej ten parametr przekazać. Tradycyjnie przyda nam się document.querySelector do złapania elementu, i addEventListener żeby powiązać kliknięcie na tym przycisku z naszą funkcją.

Co nam wyszło?

document.querySelector('#number1').addEventListener( 'click',
function () {
    numberKeyPressed( 1 );
});

Działa?

To zróbmy teraz to samo dla pozostałych 9 przycisków

Najlepiej do tego celu będzie użyć pętli.

Szybka ściągawka jak wygląda pętla

for (warunek początkowy; warunek trwania pętli; instrukcja przejścia) {
  // wykonaj ten kod tak długo jak warunek trwania pętli jest prawdziwy
}

Czyli przy 10 elementach od 0 do 9 będziemy miały:

for (let i=0; i<10; i++) {
  // wykonaj ten kod tak długo jak warunek trwania pętli jest prawdziwy
}

Składamy wszystko razem do kupy...

Przerób kod z poprzedniego etapu, tak żeby przypisywał wywołanie funkcji numberKeyPressed z odpowiednim parametrem do każdego z przycisków od 0 do 9. Wykorzystaj w tym celu pętlę, i pamiętaj o użyciu odpowiednich selektorów.

Sprawdzamy czy działa:

for( let i = 0; i < 10; i++ ) {
    document.querySelector('#number' + i).addEventListener( 'click',
    function () {
        numberKeyPressed( i );
    });
} 

Jak wyszły testy?

To teraz przed nami najtrudniejsze zadanie

Zostały nam do oprogramowania jeszcze guziczki +, -, x i /. Zastanówmy się przez chwilę, jaką funkcję pełnią w kalkulatorze (możemy otworzyć standardową aplikację kalkulatora do pomocy).

Zapamiętanie funkcji na potem

W momencie, gdy naciśniemy przycisk +, nie wykonujemy jeszcze operacji dodawania. Kalkulator zapamiętuje wtedy liczbę, jaką do tej pory wpisałyśmy, zapamiętuje, że następną operacją będzie dodawanie, i czeka na drugą liczbę, ktorą należy do niej dodać.

To jeszcze nie wszystko!

Jeżeli miałyśmy już wcześniej zapamiętaną jakąś operację, to wykona się ona również w momencie naciśnięcia znaku +. Niekoniecznie musi być to dodawanie - może to być zupełnie inna, zapamiętana wcześniej operacja.

Zapamiętanie funkcji na potem

Szczegóły tego, jak zapamiętujemy funkcję i kolejne liczby sobie dzisiaj darujemy - kto chce, może poczytać sobie plik calc.js w domu.

Dzisiaj skorzystamy z gotowej funkcji scheduleNextOperation( operation ), ktora przyjmuje jako parametr funkcję operującą na dwóch liczbach, i robi wszystko co trzeba, żeby potem to dodawanie mogło się wykonać.

Funkcja sumy

Na początek napiszemy samą funkcję sum( x, y ), która przyjmuje 2 liczby i zwraca ich sumę jako wynik.

Proste, prawda?

function sum ( x, y ) {
    return x + y;
} 

Możemy sprawdzić, czy wszystko dobrze działa w konsoli.

Teraz pozostaje nam oprogramować guziczek + - jeżeli ktoś go naciśnie, chcemy wywołać funkcję scheduleNextOperation i przekazać do niej funkcję, którą właśnie napisałyśmy.

Żeby móc przekazać parametr do funkcji, która obsługuje zdarzenie, musimy posłużyć się funkcją anonimową.

No to do dzieła!

Znajdź selektor przycisku +, i powiąż wydarzenie click na tym elemencie z wywołaniem funkcji scheduleNextOperation z funkcją sum jako parametrem. Użyj w tym celu metody addEventListener i funkcji anonimowej.

Mamy pewnie coś takiego:

function sum( x, y ) {
    return x + y;
}

document.querySelector("#plus").addEventListener( 'click' , function() {
  scheduleNextOperation ( sum );
});

Skoro mamy już oprogramowane guziczki z liczbami i znak plus, możemy przetestować jak to wszystko działa :-)

Wszystko działa dobrze? To została nam jeszcze jedna rzecz - zrobić to samo dla przycisków -, x i /.

Na tej samej zasadzie napiszmy funkcję, która wykona nam działanie matematyczne i zapamiętajmy ją na potem po naciśnięciu odpowiedniego przycisku.

Teraz to już będzie proste

Dopisz brakujące funkcje odejmowania, mnożenia i dzielenia, i powiąż każdą z odpowiednim przyciskiem. Uwaga, znak mnożenia w JavaScript to wcale nie jest x!

Sprawdzamy

function substract( x, y ) {
    return x - y;
}
document.querySelector("#minus").addEventListener( 'click' , function() {
  scheduleNextOperation ( substract );
});
function multiply( x, y ) {
    return x * y;
}
document.querySelector("#multiply").addEventListener( 'click' , function() {
  scheduleNextOperation ( multiply );
});
function divide( x, y ) {
    return x / y;
}
document.querySelector("#divide").addEventListener( 'click' , function() {
  scheduleNextOperation ( divide );
});

Najwyższa pora potestować nasz kalkulator :-)

To już wszystko, co przygotowaliśmy na dzisiaj. Dzięki za wspólnie spędzony czas i do zobaczenia!