Funkce v JS

Co je to funkce

Funkce je část kódu, kterou můžeme použít na různých místech kódu. Pokud děláme stejnou věc na více místech našeho kódu, tak je pravděpodobně lepší si na to vytvořit funkci a tu potom volat, než pořád opisovat stejný kód.

Funkci si můžeme vytvořit na cokoliv, třeba na výpočet obvodu kruhu nebo vypsání nějaké zprávy do konzole.

// vytvoření funkce
function pozdrav() {
    console.log("ahoj");
}

// zavolání funkce
pozdrav();

// zavolání funkce ještě jednou
pozdrav();

Vytváření funkcí

Funkci můžeme v JavaScriptu vytvořit třemi způsoby: deklarováním, přiřazením nebo přiřazením arrow funkce. Funkce jsou v JavaScriptu objekty, po jejich vytvoření je můžeme klidně předávat do jiných funkcí nebo jim dokonce přiřazovat vlastní vlastnosti. Každá funkce má vlastnost name, která uchovává její jméno.

deklarováním

Klasický způsob jak vytvořit funkci je jejím deklarováním, jak ukazuje ukázka.

function pozdrav() {
    console.log("ahoj");
}

přiřazením

Funkce se dá vytvořit i tak, že ji přiřazíme do proměnné, jak ukazuje ukázka.

const pozdrav = function() {
    console.log("ahoj");
}

přiřazením arrow funkce

V JavaScriptu můžeme vytvářet také arrow funkce. Také je můžeme uložit do proměnných.

const pozdrav = () => {
    console.log("ahoj");
}

Parametry

Funkce může přijímat nějaké parametry (hodnoty), které jí při jejím volání předáváme. S těmito parametry může pracovat a ovlivnit tak svůj výsledek.

// funkce přijímající dva parametry
function secti(cislo1, cislo2) {
    // s hodnotami, které funkci předáme může funkce pracovat
    const vysledek = cislo1 + cislo2;
    return vysledek;
}

Defaultní parametry

Pokud chceme, tak můžeme některým parametrům funkce nastavit defaultní hodnotu. Díky tomu budeme moci funkci volat s méně parametry než funkce očekává a ty zbývající dostanou defaultní hodnotu když je nepředáme.

// funkce s parametrem, který má defaultní hodnotu
const mocnina = function(zaklad, exponent = 2) {
    let vysledek = zaklad;
    for (let i = 1; i < exponent; i++) {
        vysledek = vysledek * zaklad;
    }
    return vysledek;
}

// funkce mocnina jde zavolat s jedním i dvěma parametry
const cislo1 = mocnina(5, 3);
const cislo2 = mocnina(4);

Proměnný počet parametrů

Pokud chceme nastavit abychom do funkce mohli předávat proměnné množství hodnot, tak můžeme použít array-like objekt arguments nebo rest parametr, který byl přidán v ES6. Array-like objekt arguments je k dispozici jen v normálních funkcích, v arrow funkcích ne. Pokud píšeme moderní JavaScript, tak bychom měli používat rest parametr.

Starší způsob (arguments objekt)

const soucet = function() {
    let vysledek = 0;
    // array-like objekt arguments obsahuje všechny parametry, které se funkci předali
    for (let i = 0; i < arguments.length; i++) {
        vysledek += arguments[i];
    }
    return vysledek;
}

// funkci soucet můžeme zavolat s jakýmkoliv množstvím argumentů
const cislo = soucet(3, 5, 2);

Novější způsob (rest parametr)

// předané parametry se převedou na pole se kterým může funkce pracovat
const soucet = (...cisla) => {
    let vysledek = 0;
    for (let i = 0; i < cisla.length; i++) {
        vysledek += cisla[i];
    }
    return vysledek;
}

// funkci soucet můžeme zavolat s jakýmkoliv množstvím argumentů
const cislo = soucet(3, 5, 2);

Klíčové slovo this

V JavaScriptu existuje klíčové slovo this, které má různé hodnoty podle toho kde jej používáme. V tomto seznamu je popsáno čemu se v jakých případech klíčové slovo this rovná:

  • v globálním prostoru (mimo funkci) bude klíčové slovo this odkazovat na globální objekt (window objekt)
  • v metodě (funkci, která patří objektu) bude klíčové slovo this odkazovat na objekt, který metodu volá
  • ve funkci bude při zapnutém strict módu (když na začátek scriptu napíšeme 'use strict';) klíčové slovo this undefined
  • ve funkci bude při vypnutém strict módu klíčové slovo this odkazovat na globální objekt (window objekt)
  • arrow funkce nemění čemu se klíčové slovo this bude rovnat, bude se rovnat tomu čemu se rovnala v předešlém scopu (pokud je arrow funkce například vnořená v normální funkci a je zapnutý strict mód, tak bude klíčové slovo this undefined)
  • ve funkci, kterou předáváme metodě addEventListener bude klíčové slovo this odkazovat na DOM element, kterému jsme event listener přidali
'use strict'; // zapnutí strict módu

console.log(this); // globální objekt (window)

function vypisThis {
    console.log(this); // undefined (protože je zapnutý strict mód)
}
vypisThis();

const objekt1 = {
    cislo: 0,
    zvysCislo: function() {
        this.cislo++; // this odkazuje na objekt, který jej volá
    }
};
objekt1.zvysCislo(); // this bude v metodě zvysCislo odkazovat na objekt1

const objekt2 = {
    cislo: 0
};
objekt2.zvysCislo = objekt1.zvysCislo;
objekt2.zvysCislo(); // this bude v metodě zvysCislo odkazovat na objekt2

const el = document.getElementById('tlacitko');
el.addEventListener('click', function() {
    console.log(this); // this odkazuje na element, kterému event listener patří (ale nesmíme použít arrow funkci)
});

Změnění na co má klíčové slovo this odkazovat při volání funkce

Pokud chceme při volání funkce změnit na co má klíčové slovo this odkazovat, můžeme to udělat pomocí metody call nebo apply. Metoda call přijímá jako první parametr hodnotu na kterou má klíčové slovo this odkazovat a v ostatní parametrech hodnoty, které se mají volané funkci předat jako parametry. Metoda apply to má podobně, ale narozdíl od metody call přijímá jako druhý parametr pole hodnot, které se mají volané funkci předat jako parametry.

function vypisThis(text, pocet) {
    for (let i = 0; i < pocet; i++) {
        console.log(text + " " + this);
    }
}

const objekt1 = { id: 1 };
const objekt2 = { id: 2 };

// zavolání funkce vypisThis - klíčové slovo this bude odkazovat na objekt1
vypisThis.call(objekt1, 'Klíčové slovo this:', 3);

// stejné jako metoda call, až na to že hodnoty, které se volané funkci přadají jako parametry se předávají v poli
vypisThis.apply(objekt2, ['Klíčové slovo this:', 3]);

Vytvoření nové funkce, která bude mít určeno, na co má klíčové slovo this odkazovat

Pokud si chceme předpřipravit na co bude klíčové slovo this odkazovat až později funkci zavoláme, tak k tomu můžeme použít metodu bind. Tato metoda vrací novou funkci, která má již určeno na co klíčové slovo this bude odkazovat když ji budeme volat. Na co bude v nově vytvořené funkci klíčové slovo this odkazovat předáváme metodě bind v prvním parametru. Pokud chceme, tak si můžeme v ostatních parametrech také přednastavit nějaké hodnoty, které se budou automaticky předávat jako parametry když ji budeme volat. Nemuseli bychom je tedy později předávat ručně.

function vypisThis(text, pocet) {
    for (let i = 0; i < pocet; i++) {
        console.log(text + " " + this);
    }
}

const objekt1 = { id: 1 };
const objekt2 = { id: 2 };

// vytvoření funkce pro vypsání objektu objekt1
const vypisObjekt1 = vypisThis.bind(objekt1);

// vytvoření funkce pro vypsání objektu objekt2
const vypisObjekt2 = vypisThis.bind(objekt2, 'Klíčové slovo this:');

vypisObjekt1('Klíčové slovo this:', 3);
vypisObjekt2(3); // první parametr jsme si přednastavili při vytváření funkce

Konstruktor funkce

Konstruktor funkce je funkce, kterou jsme si vytvořili za účelem vytváření objektů. Je zaběhlá konvence psát první písmeno jména konstruktor funkce velkým písmenem, abychom tak označili že se jedná o konstruktor funkci. Arrow funkce nefungují jako konstruktor funkce, protože nedostávají vlastní this hodnotu.

Pokud zavoláme funkci s klíčovým slovem new, tak tím řekneme že se má vytvořit nový prázdný objekt a aby na tento objekt ve volané funkci odkazovalo klíčové slovo this. Po dokončení funkce se objekt připojí k prototypu funkce (vytvoří se mu vlastnost __proto__ <- toto se týká spíše OOP v JavaScriptu, nebudu to tu vysvětlovat) a funkce jej vrátí, takže si tento nově vytvořený objekt můžeme uložit třeba do nějaké proměnné.

// konstuktor funkce (funkce na tvorbu objektů)
const Osoba = function(jmeno, datumNarozeni) {
    this.jmeno = jmeno;
    this.datumNarozeni = datumNarozeni;
}

// přidání metody k prototypu konstruktor funkce
// (všechny objekty vytvořené pomocí konstruktoru Osoba k ní budou mít přístup)
Osoba.prototype.vypisJmeno = function() {
    console.log(this.jmeno);
}

// vytvoření objektů pomocí konstruktor funkce Osoba
const karel = new Osoba('Karel', 2015);
const pavel = new Osoba('Pavel', 2005);

Konstruktor funkce se již v JavaScriptu moc nepoužívají protože od ES6 máme třídy, které jsou pro ně syntaktickým cukříkem. Pokud ale budeme třídy používat, myslím že je důležité chápat jak doopravdy vlastně fungují.

Closure

JavaScript má v sobě zabudovaný mechanismus, kterému se říká closure. Díky closure má funkce, která se nachází v jiné funkci vždy přístup k jejím proměnným i když tato funkce již skončila.

const vytvorPozdravFunkci = function(pozdrav) {
    // funkce vrátí novou funkci (poté skončí)
    return function(name) {
        // i když funkce vytvorPozdravFunkci již skončila, díky closure máme hodnotu proměnné pozdrav stále k dispozici
        console.log(`${pozdrav} ${name}`);
    }
}

const pozdrav1 = vytvorPozdravFunkci('Ahoj');
const pozdrav2 = vytvorPozdravFunkci('Čau');

pozdrav1("Karle"); // vypíše: "Ahoj Karle"
pozdrav2("Pavle"); // vypíše: "Čau Pavle"

IIFE

Immediately Invoked Function Expression (IIFE) je funkce, která se spustí hned jak ji deklarujeme. IIFE se používá k obalení kódu když vytváříme například nějakou knihovnu. Nechceme aby nám někdo kdo s knihovnou bude pracovat například přepsal nějaké proměnné. Pokud je obalíme do IIFE funkce, tak k nim nebude mít přístup a nebude je moci přepsat. Jak IIFE vytvořit ukazuje následující ukázka. Funguje to díky closure.


const pocitadlo = (function() {
    let pocet = 0;

    // k proměnné pocet budeme mít přístup jen přes tento objekt, který vracíme
    return {
        zvys: () => { pocet++; },
        sniz: () => { pocet--; }
        vypis: () => {
            console.log(pocet);
        }
    }
})();

// proměnná pocet zde není k dispozici

// proměnnou pocet ale můžeme měnit pomocí metod v objektu pocitadlo, který IIFE funkce vrátila
pocitadlo.zvys();
pocitadlo.vypis();

IIFE funkce se v JavaScriptu už tolik nepoužívají, ale můžeme se s nimi setkat u některých knihoven. Od ES6 máme k dispozici moduly.