Funkce

Poměrně často potřebujeme provést podobnou akci na mnoha místech skriptu.

Potřebujeme například ukázat pěkně vypadající zprávu, když se návštěvník přihlásí, odhlásí a možná někde jinde.

Funkce jsou hlavními „stavebními kameny“ programu. Umožňují, aby byl kód volán mnohokrát bez opakování.

Už jsme viděli příklady vestavěných funkcí, jako je alert(message) , prompt(message, default) a confirm(question) . Ale můžeme vytvářet i vlastní funkce.

Deklarace funkce

K vytvoření funkce můžeme použít deklaraci funkce .

Vypadá to takto:

function showMessage() {
 alert( 'Hello everyone!' );
}

function klíčové slovo je na prvním místě a poté název funkce a poté seznam parametrů mezi závorky (oddělené čárkami, ve výše uvedeném příkladu prázdné, příklady uvidíme později) a nakonec kód funkce, nazývané také „tělo funkce“, mezi složené závorky.

function name(parameter1, parameter2, ... parameterN) {
 // body
}

Naše nová funkce může být volána jejím jménem:showMessage() .

Například:

function showMessage() {
 alert( 'Hello everyone!' );
}

showMessage();
showMessage();

Volání showMessage() spustí kód funkce. Zde zprávu uvidíme dvakrát.

Tento příklad jasně ukazuje jeden z hlavních účelů funkcí:vyhnout se duplicitě kódu.

Pokud někdy potřebujeme změnit zprávu nebo způsob, jakým je zobrazena, stačí upravit kód na jednom místě:ve funkci, která jej zobrazuje.

Místní proměnné

Proměnná deklarovaná uvnitř funkce je viditelná pouze uvnitř této funkce.

Například:

function showMessage() {
 let message = "Hello, I'm JavaScript!"; // local variable

 alert( message );
}

showMessage(); // Hello, I'm JavaScript!

alert( message ); // <-- Error! The variable is local to the function

Vnější proměnné

Funkce může také přistupovat k vnější proměnné, například:

let userName = 'John';

function showMessage() {
 let message = 'Hello, ' + userName;
 alert(message);
}

showMessage(); // Hello, John

Funkce má plný přístup k vnější proměnné. Může jej také upravit.

Například:

let userName = 'John';

function showMessage() {
 userName = "Bob"; // (1) changed the outer variable

 let message = 'Hello, ' + userName;
 alert(message);
}

alert( userName ); // John before the function call

showMessage();

alert( userName ); // Bob, the value was modified by the function

Vnější proměnná se používá pouze v případě, že neexistuje žádná místní.

Pokud je uvnitř funkce deklarována proměnná se stejným názvem, pak stíní ten vnější. Například v kódu níže funkce používá místní userName . Vnější je ignorována:

let userName = 'John';

function showMessage() {
 let userName = "Bob"; // declare a local variable

 let message = 'Hello, ' + userName; // Bob
 alert(message);
}

// the function will create and use its own userName
showMessage();

alert( userName ); // John, unchanged, the function did not access the outer variable
Globální proměnné

Proměnné deklarované mimo jakoukoli funkci, jako je vnější userName ve výše uvedeném kódu se nazývají globální .

Globální proměnné jsou viditelné z jakékoli funkce (pokud nejsou zastíněny místními).

Je dobrým zvykem minimalizovat použití globálních proměnných. Moderní kód má málo globálních nebo žádné. Většina proměnných spočívá v jejich funkcích. Někdy však mohou být užitečné pro ukládání dat na úrovni projektu.

Parametry

Pomocí parametrů můžeme funkcím předávat libovolná data.

V níže uvedeném příkladu má funkce dva parametry:from a text .

function showMessage(from, text) { // parameters: from, text
 alert(from + ': ' + text);
}

showMessage('Ann', 'Hello!'); // Ann: Hello! (*)
showMessage('Ann', "What's up?"); // Ann: What's up? (**)

Když je funkce volána v řádcích (*) a (**) , dané hodnoty se zkopírují do lokálních proměnných from a text . Poté je funkce použije.

Zde je ještě jeden příklad:máme proměnnou from a předat ji funkci. Poznámka:funkce se mění from , ale změna není vidět venku, protože funkce vždy získá kopii hodnoty:

function showMessage(from, text) {

 from = '*' + from + '*'; // make "from" look nicer

 alert( from + ': ' + text );
}

let from = "Ann";

showMessage(from, "Hello"); // *Ann*: Hello

// the value of "from" is the same, the function modified a local copy
alert( from ); // Ann

Když je hodnota předána jako parametr funkce, nazývá se také argument .

Jinými slovy, abych uvedl tyto pojmy na pravou míru:

  • Parametr je proměnná uvedená v závorkách v deklaraci funkce (je to časový termín deklarace).
  • Argument je hodnota, která je předána funkci při jejím volání (je to termín doby volání).

Deklarujeme funkce se seznamem jejich parametrů a pak je nazýváme předáváním argumentů.

Ve výše uvedeném příkladu by se dalo říci:„funkce showMessage je deklarován se dvěma parametry, pak volán se dvěma argumenty:from a "Hello" ".

."

Výchozí hodnoty

Pokud je funkce volána, ale není zadán argument, pak se odpovídající hodnota stane undefined .

Například výše zmíněná funkce showMessage(from, text) lze volat s jediným argumentem:

showMessage("Ann");

To není chyba. Výsledkem takového volání by bylo "*Ann*: undefined" . Jako hodnota pro text není předán, stane se undefined .

Pomocí = můžeme zadat takzvanou „výchozí“ hodnotu parametru v deklaraci funkce (použije se, pokud je vynechána). :

function showMessage(from, text = "no text given") {
 alert( from + ": " + text );
}

showMessage("Ann"); // Ann: no text given

Nyní, pokud text není předán, získá hodnotu "no text given" .

Výchozí hodnota také naskočí, pokud parametr existuje, ale striktně se rovná undefined , takto:

showMessage("Ann", undefined); // Ann: no text given

Zde "no text given" je řetězec, ale může to být i složitější výraz, který se vyhodnotí a přiřadí pouze v případě, že parametr chybí. Takže je to také možné:

function showMessage(from, text = anotherFunction()) {
 // anotherFunction() only executed if no text given
 // its result becomes the value of text
}
Vyhodnocení výchozích parametrů

V JavaScriptu je výchozí parametr vyhodnocen pokaždé, když je funkce volána bez příslušného parametru.

Ve výše uvedeném příkladu anotherFunction() není vůbec voláno, pokud text je poskytnut parametr.

Na druhou stranu je nezávisle voláno pokaždé, když je text chybí.

Výchozí parametry ve starém kódu JavaScript

Před několika lety JavaScript nepodporoval syntaxi výchozích parametrů. Lidé tedy používali jiné způsoby, jak je specifikovat.

V dnešní době se s nimi můžeme setkat ve starých skriptech.

Například explicitní kontrola undefined :

function showMessage(from, text) {
 if (text === undefined) {
 text = 'no text given';
 }

 alert( from + ": " + text );
}

…Nebo pomocí || operátor:

function showMessage(from, text) {
 // If the value of text is falsy, assign the default value
 // this assumes that text == "" is the same as no text at all
 text = text || 'no text given';
 ...
}

Alternativní výchozí parametry

Někdy má smysl přiřadit výchozí hodnoty parametrům nikoli v deklaraci funkce, ale až později.

Můžeme zkontrolovat, zda je parametr předán během provádění funkce, porovnáním s undefined :

function showMessage(text) {
 // ...

 if (text === undefined) { // if the parameter is missing
 text = 'empty message';
 }

 alert(text);
}

showMessage(); // empty message

…Nebo bychom mohli použít || operátor:

function showMessage(text) {
 // if text is undefined or otherwise falsy, set it to 'empty'
 text = text || 'empty';
 ...
}

Moderní JavaScriptové motory podporují nulový slučovací operátor ?? , je lepší, když většina chybných hodnot, jako je 0 , by měl být považován za „normální“:

function showCount(count) {
 // if count is undefined or null, show "unknown"
 alert(count ?? "unknown");
}

showCount(0); // 0
showCount(null); // unknown
showCount(); // unknown

Vrácení hodnoty

Funkce může jako výsledek vrátit hodnotu zpět do volajícího kódu.

Nejjednodušším příkladem by byla funkce, která sečte dvě hodnoty:

function sum(a, b) {
 return a + b;
}

let result = sum(1, 2);
alert( result ); // 3

Direktiva return může být na libovolném místě funkce. Když ho vykonání dosáhne, funkce se zastaví a hodnota se vrátí do volajícího kódu (přiřazeno result výše).

return může mít mnoho výskytů v jediné funkci. Například:

function checkAge(age) {
 if (age >= 18) {
 return true;
 } else {
 return confirm('Do you have permission from your parents?');
 }
}

let age = prompt('How old are you?', 18);

if ( checkAge(age) ) {
 alert( 'Access granted' );
} else {
 alert( 'Access denied' );
}

Je možné použít return bez hodnoty. To způsobí okamžité ukončení funkce.

Například:

function showMovie(age) {
 if ( !checkAge(age) ) {
 return;
 }

 alert( "Showing you the movie" ); // (*)
 // ...
}

Ve výše uvedeném kódu, pokud checkAge(age) vrátí false a poté showMovie nebude pokračovat na alert .

Funkce s prázdným return nebo bez ní vrátí undefined

Pokud funkce nevrátí hodnotu, je to stejné, jako kdyby vrátila undefined :

function doNothing() { /* empty */ }

alert( doNothing() === undefined ); // true

Prázdné return je také stejné jako return undefined :

function doNothing() {
 return;
}

alert( doNothing() === undefined ); // true
Nikdy nepřidávejte nový řádek mezi return a hodnotu

Pro dlouhý výraz v return , může být lákavé dát to na samostatný řádek, jako je tento:

return
 (some + long + expression + or + whatever * f(a) + f(b))

To nefunguje, protože JavaScript předpokládá středník za return . Bude to fungovat stejně jako:

return;
 (some + long + expression + or + whatever * f(a) + f(b))

Takže se fakticky stává prázdným návratem.

Pokud chceme, aby se vrácený výraz zalomil přes více řádků, měli bychom jej začít na stejném řádku jako return . Nebo tam alespoň vložte úvodní závorku takto:

return (
 some + long + expression
 + or +
 whatever * f(a) + f(b)
 )

A bude fungovat přesně tak, jak očekáváme.

Pojmenování funkce

Funkce jsou akce. Jejich jméno je tedy obvykle sloveso. Měl by být stručný, co nejpřesnější a měl by popisovat, co funkce dělá, aby někdo, kdo čte kód, získal informaci o tom, co funkce dělá.

Je rozšířenou praxí začít funkci slovní předponou, která nejasně popisuje akci. V rámci týmu musí existovat dohoda o významu předpon.

Například funkce, které začínají "show" obvykle něco ukázat.

Funkce začínající…

  • "get…" – vrátí hodnotu,
  • "calc…" – něco vypočítat,
  • "create…" – něco vytvořit,
  • "check…" – něco zkontrolovat a vrátit boolean atd.

Příklady takových jmen:

showMessage(..) // shows a message
getAge(..) // returns the age (gets it somehow)
calcSum(..) // calculates a sum and returns the result
createForm(..) // creates a form (and usually returns it)
checkPermission(..) // checks a permission, returns true/false

Když jsou předpony na místě, pohled na název funkce umožňuje pochopit, jaký druh práce vykonává a jakou hodnotu vrací.

Jedna funkce – jedna akce

Funkce by měla dělat přesně to, co naznačuje její název, nic víc.

Dvě nezávislé akce si obvykle zaslouží dvě funkce, i když jsou obvykle volány společně (v tom případě můžeme vytvořit 3. funkci, která volá tyto dvě).

Několik příkladů porušení tohoto pravidla:

  • getAge – bylo by špatné, kdyby ukazoval alert s věkem (měl by jen dostat).
  • createForm – bylo by špatné, kdyby upravila dokument a přidala k němu formulář (měla by jej pouze vytvořit a vrátit).
  • checkPermission – bylo by špatné, kdyby zobrazoval access granted/denied zprávu (měl by pouze provést kontrolu a vrátit výsledek).

Tyto příklady předpokládají běžné významy předpon. Vy a váš tým se můžete svobodně dohodnout na jiných významech, ale obvykle se příliš neliší. V každém případě byste měli dobře rozumět tomu, co předpona znamená, co funkce s předponou může a co nemůže dělat. Všechny funkce se stejnou předponou by se měly řídit pravidly. A tým by měl sdílet znalosti.

Ultrakrátké názvy funkcí

Funkce, které se používají velmi často někdy mají ultrakrátká jména.

Například framework jQuery definuje funkci s $ . Knihovna Lodash má svou základní funkci nazvanou _ .

To jsou výjimky. Obecně by názvy funkcí měly být stručné a popisné.

Funkce ==Komentáře

Funkce by měly být krátké a dělat přesně jednu věc. Pokud je to velké, možná stojí za to rozdělit funkci na několik menších funkcí. Někdy nemusí být dodržování tohoto pravidla tak snadné, ale rozhodně je to dobré.

Samostatnou funkci lze nejen snadněji testovat a ladit – její samotná existence je skvělým komentářem!

Porovnejte například dvě funkce showPrimes(n) níže. Každý z nich zobrazuje prvočísla až do n .

První varianta používá štítek:

function showPrimes(n) {
 nextPrime: for (let i = 2; i < n; i++) {

 for (let j = 2; j < i; j++) {
 if (i % j == 0) continue nextPrime;
 }

 alert( i ); // a prime
 }
}

Druhá varianta využívá doplňkovou funkci isPrime(n) otestovat primálnost:

function showPrimes(n) {

 for (let i = 2; i < n; i++) {
 if (!isPrime(i)) continue;

 alert(i); // a prime
 }
}

function isPrime(n) {
 for (let i = 2; i < n; i++) {
 if ( n % i == 0) return false;
 }
 return true;
}

Druhá varianta je srozumitelnější, že? Místo části kódu vidíme název akce (isPrime ). Někdy lidé označují takový kód jako sebepopisující .

Funkce lze tedy vytvářet, i když je nehodláme znovu používat. Strukturují kód a činí jej čitelným.

Shrnutí

Deklarace funkce vypadá takto:

function name(parameters, delimited, by, comma) {
 /* code */
}
  • Hodnoty předané funkci jako parametry jsou zkopírovány do jejích lokálních proměnných.
  • Funkce může přistupovat k vnějším proměnným. Ale funguje to pouze zevnitř ven. Kód mimo funkci nevidí své lokální proměnné.
  • Funkce může vrátit hodnotu. Pokud tomu tak není, jeho výsledek je undefined .

Aby byl kód čistý a srozumitelný, doporučuje se ve funkci používat hlavně lokální proměnné a parametry, nikoli vnější proměnné.

Vždy je snazší pochopit funkci, která získává parametry, pracuje s nimi a vrací výsledek, než funkci, která nezíská žádné parametry, ale jako vedlejší efekt upravuje vnější proměnné.

Pojmenování funkce:

  • Název by měl jasně popisovat, co funkce dělá. Když v kódu vidíme volání funkce, dobrý název nám okamžitě umožní pochopit, co dělá a vrací.
  • Funkce je akce, takže názvy funkcí jsou obvykle verbální.
  • Existuje mnoho známých předpon funkcí jako create… , show… , get… , check… a tak dále. Použijte je k naznačení toho, co funkce dělá.

Funkce jsou hlavními stavebními kameny skriptů. Nyní jsme probrali základy, takže je vlastně můžeme začít vytvářet a používat. Ale to je jen začátek cesty. Budeme se k nim mnohokrát vracet a podrobněji prozkoumat jejich pokročilé funkce.


No