JavaScript funkcie. Expresívny JavaScript: Funkcie JavaScriptu vracajúce viacero hodnôt z funkcie

Ľudia si myslia, že informatika je umenie pre géniov. V skutočnosti je to naopak – len veľa ľudí vyrába veci, ktoré stoja na sebe, ako keby vytvárali stenu z malých kamienkov.

Donald Knuth

Volania funkcií, ako je napríklad upozornenie, ste už videli. Funkcie sú chlebom a maslom programovania v JavaScripte. Myšlienka zabaliť časť programu a nazvať ho ako premenná je veľmi populárna. Je to nástroj na štruktúrovanie veľkých programov, redukciu opakovania, pomenovanie podprogramov a izoláciu podprogramov od seba.

Najzrejmejším použitím funkcií je vytvorenie nového slovníka. Vymýšľanie slov pre obyčajnú ľudskú prózu je zlá forma. To je potrebné v programovacom jazyku.

Priemerný dospelý hovoriaci po rusky pozná približne 10 000 slov. Vzácny programovací jazyk obsahuje 10 000 vstavaných príkazov. A slovná zásoba programovacieho jazyka je jasnejšie definovaná, takže je menej flexibilná ako ľudský. Preto k nemu väčšinou musíme pridať vlastné slová, aby sme sa vyhli zbytočnému opakovaniu.

Definícia funkcie Definícia funkcie je definícia normálnej premennej, kde hodnota, ktorú premenná prijíma, je funkcia. Napríklad nasledujúci kód definuje premennú štvorec, ktorá odkazuje na funkciu, ktorá vypočíta druhú mocninu daného čísla:

Var square = funkcia (x) ( return x * x; ); console.log(square(12)); // → 144

Funkciu vytvára výraz začínajúci kľúčovým slovom function. Funkcie majú sadu parametrov (v tomto prípade iba x) a telo obsahujúce inštrukcie, ktoré sa musia vykonať pri volaní funkcie. Telo funkcie je vždy uzavreté v zložených zátvorkách, aj keď pozostáva z jedného príkazu.

Funkcia môže mať niekoľko parametrov alebo vôbec žiadne. V nasledujúcom príklade makeNoise nemá zoznam parametrov, ale výkon má dva:

Var makeNoise = function() ( console.log("Hovno!"); ); Robit hluk(); // → Khrya! var mocnina = funkcia (základ, exponent) ( var výsledok = 1; pre (počet var = 0; počet< exponent; count++) result *= base; return result; }; console.log(power(2, 10)); // → 1024

Niektoré funkcie vracajú hodnotu, napríklad mocnina a štvorec, iné nie, napríklad makeNoise, čo má len vedľajší efekt. Príkaz return špecifikuje hodnotu vrátenú funkciou. Keď spracovanie programu dosiahne túto inštrukciu, okamžite opustí funkciu a vráti túto hodnotu na miesto v kóde, z ktorého bola funkcia volaná. return bez výrazu vracia undefined .

Parametre a rozsah Parametre funkcie sú rovnaké premenné, ale ich počiatočné hodnoty sa nastavujú pri volaní funkcie a nie v jej kóde.

Dôležitou vlastnosťou funkcií je, že premenné vytvorené v rámci funkcie (vrátane parametrov) sú lokálne pre danú funkciu. To znamená, že v mocenskom príklade bude výsledná premenná vytvorená pri každom volaní funkcie a tieto jednotlivé jej inkarnácie nebudú mať navzájom nič spoločné.

Táto premenná lokalita sa vzťahuje len na parametre a premenné vytvorené v rámci funkcií. Premenné definované mimo akejkoľvek funkcie sa nazývajú globálne, pretože sú viditeľné v celom programe. K takýmto premenným môžete pristupovať aj vo funkcii, pokiaľ nedeklarujete lokálnu premennú s rovnakým názvom.

Ilustruje to nasledujúci kód. Definuje a volá dve funkcie, ktoré priraďujú hodnotu premennej x. Prvý ju deklaruje ako lokálnu, čím zmení iba lokálnu premennú. Druhý nedeklaruje, takže práca s x vo vnútri funkcie odkazuje na globálnu premennú x definovanú na začiatku príkladu.

Var x = "vonku"; var f1 = funkcia() ( var x = "vo vnútri f1"; ); f1(); console.log(x); // → mimo var f2 = function() ( x = "vo vnútri f2"; ); f2(); console.log(x); // → vnútri f2

Toto správanie pomáha predchádzať náhodným interakciám medzi funkciami. Ak by sa všetky premenné použili kdekoľvek v programe, bolo by veľmi ťažké zabezpečiť, aby sa jedna premenná nepoužívala na rôzne účely. A ak by ste premennú znova použili, narazili by ste na zvláštne efekty, keď kód tretej strany poškodil hodnoty vašej premennej. Spracovaním funkčných lokálnych premenných tak, aby existovali iba v rámci funkcie, jazyk umožňuje pracovať s funkciami, akoby išlo o samostatné malé vesmíry, čo vám umožňuje nestarať sa o celý kód.

Vnorený rozsah JavaScript rozlišuje viac než len medzi globálnymi a lokálnymi premennými. Funkcie možno definovať v rámci funkcií, výsledkom čoho sú viaceré úrovne lokality.

Napríklad nasledujúca dosť nezmyselná funkcia obsahuje vo vnútri ďalšie dve:

Premenná krajina = funkcia () ( var result = ""; var flat = funkcia (veľkosť) ( for (var count = 0; počet< size; count++) result += "_"; }; var mountain = function(size) { result += "/"; for (var count = 0; count < size; count++) result += """; result += "\\"; }; flat(3); mountain(4); flat(6); mountain(1); flat(1); return result; }; console.log(landscape()); // → ___/""""\______/"\_

Funkcie flat a mountain vidia výslednú premennú, pretože sú vo funkcii, ktorá ju definuje. Nemôžu však vidieť navzájom početné premenné, pretože premenné jednej funkcie sú mimo rozsahu druhej. A prostredie mimo funkcie krajiny nevidí žiadnu z premenných definovaných vo vnútri tejto funkcie.

Stručne povedané, v rámci každého lokálneho rozsahu môžete vidieť všetky rozsahy, ktoré ho obsahujú. Množina premenných dostupných v rámci funkcie je určená miestom, kde je funkcia deklarovaná v programe. Všetky premenné z blokov obklopujúcich definíciu funkcie sú viditeľné – vrátane tých, ktoré sú definované na najvyššej úrovni v hlavnom programe. Tento prístup k rozsahom sa nazýva lexikálny.

Ľudia, ktorí študovali iné programovacie jazyky, si môžu myslieť, že každý blok uzavretý v zložených zátvorkách vytvára svoje vlastné lokálne prostredie. Ale v JavaScripte vytvárajú rozsah iba funkcie. Môžete použiť samostatne stojace bloky:

Var niečo = 1; ( var nieco = 2; // Urobte nieco s premennou nieco... ) // Opustil blok...

Ale niečo vo vnútri bloku je rovnaká premenná ako vonku. Aj keď sú takéto bloky povolené, má zmysel ich používať iba pre príkazy if a cykly.

Ak sa vám to zdá zvláštne, nie ste jediný, komu to pripadá. Vo verzii JavaScript 1.7 sa objavil kľúčové slovo let, ktorý funguje ako var, ale vytvára premenné, ktoré sú lokálne pre akýkoľvek daný blok, nielen pre funkciu.

Funkcie ako hodnoty Názvy funkcií sa zvyčajne používajú ako názov časti programu. Takáto premenná sa nastaví raz a nemení sa. Je teda ľahké pomýliť si funkciu a jej názov.

Ale to sú dve rozdielne veci. Volanie funkcie možno použiť ako jednoduchú premennú – napríklad použiť v akomkoľvek výraze. Je možné uložiť volanie funkcie do novej premennej, odovzdať ho ako parameter inej funkcii atď. Tiež premenná uchovávajúca volanie funkcie zostáva regulárnou premennou a jej hodnotu možno zmeniť:

Var launchMissiles = function(value) ( ​​​​missileSystem.launch("alebo!"); ); if (safeMode) launchMissiles = function(value) (/* cancel */);

V kapitole 5 budeme diskutovať o úžasných veciach, ktoré môžete urobiť odovzdávaním volaní funkcií iným funkciám.

Deklarovanie funkcií Existuje kratšia verzia výrazu „var square = funkcia...“. Kľúčové slovo funkcie možno použiť na začiatku príkazu:

Funkcia square(x) ( return x * x; )

Toto je deklarácia funkcie. Príkaz definuje štvorcovú premennú a priraďuje jej danú funkciu. Zatiaľ je všetko dobré. Takáto definícia má len jedno úskalie.

Console.log("Budúcnosť hovorí:", future()); function future() ( return "STÁLE nemáme žiadne lietajúce autá."; )

Tento kód funguje, aj keď je funkcia deklarovaná pod kódom, ktorý ju používa. Je to preto, že deklarácie funkcií nie sú súčasťou bežného vykonávania programu zhora nadol. Sú „presunuté“ na vrchol svojho rozsahu a môžu byť vyvolané akýmkoľvek kódom v tomto rozsahu. Niekedy je to výhodné, pretože kód môžete napísať v poradí, ktoré dáva najväčší zmysel, bez toho, aby ste sa museli obávať, že budete musieť definovať všetky funkcie vyššie, kde sa používajú.

Čo sa stane, ak umiestnime deklaráciu funkcie do podmieneného bloku alebo slučky? Nemusíte to robiť. Historicky rôzne platformy spúšťajúce JavaScript riešili takéto prípady odlišne a súčasný jazykový štandard to zakazuje. Ak chcete, aby sa vaše programy spúšťali sekvenčne, použite deklarácie funkcií iba v rámci iných funkcií alebo hlavného programu.

Príklad funkcie() ( funkcia a() () // Normálne, ak (niečo) ( funkcia b() () // Ay-yay-yay! ) )

Zásobník volaní Je užitočné pozrieť sa bližšie na to, ako príkaz na vykonanie funguje s funkciami. Tu jednoduchý program s viacerými volaniami funkcií:

Funkcia pozdrav(kto) ( console.log("Ahoj, " + kto); ) pozdrav("Semyon"); console.log("Pokeda");

Spracuje sa asi takto: volanie pozdravu spôsobí skok na začiatok funkcie. Zavolá vstavanú funkciu console.log, ktorá zachytí kontrolu, urobí svoju vec a vráti kontrolu. Potom sa dostane na koniec pozdravu a vráti sa na miesto, odkiaľ ho zavolali. Ďalší riadok opäť volá console.log.

Schematicky to možno znázorniť takto:

Top pozdrav console.log pozdrav top console.log top

Pretože sa funkcia musí vrátiť na miesto, odkiaľ bola volaná, počítač si musí zapamätať kontext, z ktorého bola funkcia volaná. V jednom prípade by sa mal console.log vrátiť späť na pozdrav. V inom sa vracia na koniec programu.

Miesto, kde si počítač pamätá kontext, sa nazýva zásobník. Pri každom volaní funkcie sa aktuálny kontext presunie na začiatok zásobníka. Keď sa funkcia vráti, vytiahne horný kontext zo zásobníka a použije ho na pokračovanie v behu.

Zásobník vyžaduje miesto v pamäti. Keď sa zásobník príliš zväčší, počítač prestane vykonávať a povie niečo ako „pretečenie zásobníka“ alebo „príliš veľa rekurzie“. Demonštruje to nasledujúci kód – kladie počítaču veľmi zložitú otázku, ktorá vedie k nekonečným skokom medzi dvoma funkciami. Presnejšie povedané, išlo by o nekonečné skoky, ak by mal počítač nekonečný zásobník. V skutočnosti zásobník pretečie.

Funkcia chicken() ( return egg(); ) function egg() ( return chicken(); ) console.log(chicken() + "prišlo prvé."); // → ??

Voliteľné argumenty Nasledujúci kód je úplne legálny a funguje bez problémov:

Alert("Ahoj", "Dobrý večer", "Ahoj všetci!");

Oficiálne má funkcia jeden argument. Keď ju však takto vyzvú, nesťažuje sa. Ostatné argumenty ignoruje a ukáže "Ahoj."

JavaScript je veľmi špecifický, pokiaľ ide o počet argumentov odovzdaných funkcii. Ak prenesiete príliš veľa, nadbytočné budú ignorované. Príliš málo a chýbajúce budú mať priradenú hodnotu nedefinovanú.

Nevýhodou tohto prístupu je, že je možné – a dokonca pravdepodobné – odovzdať funkcii nesprávny počet argumentov bez toho, aby sa na to niekto sťažoval.

Výhodou je, že môžete vytvárať funkcie, ktoré preberajú voliteľné argumenty. Napríklad v ďalšej verzii mocninnej funkcie ju možno volať buď s dvoma alebo s jedným argumentom - v druhom prípade sa exponent bude rovnať dvom a funkcia funguje ako štvorec.

Mocnina funkcie (základ, exponent) ( ak (exponent == nedefinovaný) exponent = 2; výsledok var = 1; pre (počet var = 0; počet< exponent; count++) result *= base; return result; } console.log(power(4)); // → 16 console.log(power(4, 3)); // → 64

V ďalšej kapitole uvidíme, ako môžete v tele funkcie zistiť presný počet argumentov, ktoré jej boli odovzdané. Je to užitočné, pretože... umožňuje vytvoriť funkciu, ktorá má ľubovoľný počet argumentov. Napríklad console.log používa túto vlastnosť a vypíše všetky argumenty, ktoré sú jej odovzdané:

Console.log("R", 2, "D", 2); // → R 2 D 2

Závery Možnosť používať volania funkcií ako premenné spolu so skutočnosťou, že lokálne premenné sa vytvárajú nanovo pri každom volaní funkcie, nás vedie k zaujímavej otázke. Čo sa stane s lokálnymi premennými, keď funkcia prestane fungovať?

Nasledujúci príklad ilustruje tento problém. Deklaruje funkciu wrapValue, ktorá vytvorí lokálnu premennú. Potom vráti funkciu, ktorá prečíta túto lokálnu premennú a vráti jej hodnotu.

Funkcia wrapValue(n) ( var localVariable = n; return function() ( return localVariable; ); ) var wrap1 = wrapValue(1); var wrap2 = wrapValue(2); console.log(wrap1()); // → 1 console.log(wrap2()); // → 2

Toto platí a funguje ako má – prístup k premennej zostáva zachovaný. Okrem toho môže súčasne existovať viacero inštancií tej istej premennej, čo ďalej potvrdzuje skutočnosť, že pri každom volaní funkcie sa znova vytvárajú lokálne premenné.

Táto schopnosť pracovať s odkazom na inštanciu lokálnej premennej sa nazýva uzáver. Funkcia, ktorá uzatvára lokálne premenné, sa nazýva uzáver. Nielenže vás zbaví starostí s premenlivou životnosťou, ale tiež vám umožní kreatívne využívať funkcie.

S miernou úpravou zmeníme náš príklad na funkciu, ktorá násobí čísla ľubovoľným daným číslom.

Násobiteľ funkcie (faktor) ( návratová funkcia (číslo) ( návratové číslo * faktor; ); ) var dvakrát = multiplikátor(2); console.log(dvakrát(5)); // → 10

Samostatná premenná ako localVariable z príkladu wrapValue už nie je potrebná. Pretože parameter je sám o sebe lokálna premenná.

Začať myslieť týmto spôsobom si bude vyžadovať prax. Dobrá voľba mentálny model – predstavte si, že funkcia zmrazí kód vo svojom tele a zabalí ho do obalu. Keď uvidíte funkciu return(...) (...), predstavte si ju ako ovládací panel pre časť kódu zmrazenú na neskoršie použitie.

V našom príklade multiplikátor vráti zmrazený kus kódu, ktorý uložíme do premennej double. Posledný riadok volá funkciu obsiahnutú v premennej, ktorá spôsobí aktiváciu uloženého kódu (návratové číslo * faktor;). Stále má prístup k premennej faktora, ktorá bola definovaná pri volaní multiplikátora, a má tiež prístup k argumentu odovzdanému počas odmrazovania (5) ako číselnému parametru.

Rekurzia Funkcia sa môže volať sama, pokiaľ sa stará o to, aby nepreplnila zásobník. Táto funkcia sa nazýva rekurzívna. Tu je príklad alternatívnej implementácie umocňovania:

Funkcia power(základ, exponent) ( if (exponent == 0) return 1; else return base * power(base, exponent - 1); ) console.log(power(2, 3)); // → 8

Zhruba takto matematici definujú umocňovanie a možno to vystihuje koncept elegantnejšie ako cyklus. Funkcia sa mnohokrát volá s rôznymi argumentmi, aby sa dosiahlo viacnásobné násobenie.

S touto implementáciou je však problém: normálne prostredie JavaScript je 10-krát pomalší ako verzia so slučkou. Prechádzka po slučke je lacnejšia ako volanie funkcie.

Dilema rýchlosť verzus elegancia je celkom zaujímavá. Existuje určitá priepasť medzi pohodlnosťou pre ľudí a pohodlnosťou pre stroje. Akýkoľvek program možno urýchliť tým, že bude väčší a zložitejší. Od programátora sa vyžaduje, aby našiel vhodnú rovnováhu.

V prípade prvého umocnenia je neelegantná slučka celkom jednoduchá a priamočiara. Nemá zmysel ho nahrádzať rekurziou. Často sa však programy zaoberajú tak zložitými konceptmi, že človek chce znížiť efektivitu zvýšením čitateľnosti.

Základným pravidlom, ktoré sa už viackrát zopakovalo a s ktorým úplne súhlasím, je nestarať sa o výkon, kým si nie ste úplne istí, že sa program spomaľuje. Ak áno, nájdite diely, ktoré vydržia najdlhšie a vymeňte eleganciu za efektívnosť.

Samozrejme, nemali by sme hneď úplne ignorovať výkon. V mnohých prípadoch, podobne ako pri umocňovaní, z elegantných riešení veľa jednoduchosti nedostaneme. Niekedy skúsený programátor okamžite vidí, že jednoduchý prístup nikdy nebude dostatočne rýchly.

Upozorňujem na to, pretože príliš veľa začínajúcich programátorov je posadnutých efektívnosťou aj v malých veciach. Výsledok je väčší, komplexnejší a často nie bez chýb. Takéto programy sa píšu dlhšie, ale často nefungujú oveľa rýchlejšie.

Ale rekurzia nie je vždy len menej efektívnou alternatívou k slučkám. Niektoré problémy sa dajú ľahšie vyriešiť rekurziou. Najčastejšie ide o prechod niekoľkých vetiev stromu, z ktorých každá sa môže vetviť.

Tu je hádanka: môžete získať nekonečný počet čísel tak, že začnete číslom 1 a potom buď sčítate 5, alebo vynásobíte číslom 3. Ako napíšeme funkciu, ktorá sa pri zadaní čísla snaží nájsť postupnosť sčítania a násobenia ktoré vedú k danému číslu? Napríklad číslo 13 možno získať tak, že najprv vynásobíte 1 3 a potom dvakrát pridáte 5. A číslo 15 sa takto vôbec nedá získať.

Rekurzívne riešenie:

Funkcia findSolution(target) ( funkcia find(start, history) ( if (start == target) return history; else if (start > target) return null; else return find (start + 5, "(" + history + " + 5)") || find(začiatok * 3, "(" + história + " * 3)"); ) return find(1, "1"); ) console.log(findSolution(24)); // → (((1 * 3) + 5) * 3)

Tento príklad nemusí nevyhnutne nájsť najkratšie riešenie – vyhovuje mu akékoľvek. Neočakávam, že okamžite pochopíte, ako program funguje. Poďme však pochopiť toto skvelé cvičenie v rekurzívnom myslení.

Vnútorná funkcia find robí rekurziu. Vyžaduje si to dva argumenty – aktuálne číslo a reťazec, ktorý obsahuje záznam o tom, ako sme k tomuto číslu dospeli. A vráti buď reťazec zobrazujúci našu postupnosť krokov, alebo hodnotu null.

Na tento účel funkcia vykoná jednu z troch akcií. Ak sa dané číslo rovná cieľu, tak aktuálny príbeh je práve spôsob, ako ho dosiahnuť, takže sa vracia. Ak je dané číslo väčšie ako cieľ, nemá zmysel pokračovať v násobení a pridávaní, pretože sa bude len zvyšovať. A ak sme ešte nedosiahli cieľ, funkcia skúša oboje možné spôsoby, počnúc daným číslom. Privoláva sa dvakrát, pri každej metóde raz. Ak prvé volanie nevráti hodnotu null, vráti sa. V inom prípade sa vráti druhý.

Aby sme lepšie pochopili, ako funkcia dosahuje požadovaný efekt, pozrime sa na volania, ktoré vykonáva, aby sme našli riešenie čísla 13.

Nájsť (1, "1") nájsť (6, "(1 + 5)") nájsť (11, "((1 + 5) + 5)") nájsť (16, "((1 + 5) + 5 ) + 5)") príliš veľký nález(33, "(((1 + 5) + 5) * 3)") príliš veľký nález(18, "(1 + 5) * 3)") príliš veľký nález( 3, "(1 * 3)") nájsť (8, "(1 * 3) + 5)") nájsť (13, "(((1 * 3) + 5) + 5)") nájsť!

Odsadenie zobrazuje hĺbku zásobníka hovorov. Prvýkrát sa funkcia find zavolá dvakrát, aby skontrolovala riešenia začínajúce (1 + 5) a (1 * 3). Prvý hovor hľadá riešenie začínajúce na (1 + 5) a používa rekurziu na kontrolu všetkých riešení, ktoré produkujú číslo menšie alebo rovné požadovanému číslu. Nenájde a vráti hodnotu null. Potom operátor || a prejde na volanie funkcie, ktoré skúma možnosť (1 * 3). Tu máme šťastie, pretože v treťom rekurzívnom volaní dostaneme 13. Toto volanie vráti reťazec a každý z || pozdĺž cesty prechádza cez túto čiaru vyššie, čo vedie k vráteniu riešenia.

Rastúce funkcie Existujú dva viac-menej prirodzené spôsoby zavádzania funkcií do programu.

Prvým je, že podobný kód napíšete niekoľkokrát. Tomuto sa treba vyhnúť – viac kódu znamená viac priestoru pre chyby a viac materiálu na čítanie pre tých, ktorí sa snažia program pochopiť. Takže vezmeme opakujúcu sa funkčnosť a prispôsobíme ju dobré meno a uviesť ho do funkcie.

Druhým spôsobom je, že objavíte potrebu nejakej novej funkcionality, ktorá si zaslúži umiestnenie v samostatnej funkcii. Začnete názvom funkcie a potom napíšete jej telo. Môžete dokonca začať napísaním kódu, ktorý používa funkciu, ešte pred definovaním samotnej funkcie.

To, aké ťažké je pre vás pomenovať funkciu, ukazuje, ako dobre rozumiete jej funkciám. Vezmime si príklad. Musíme napísať program, ktorý vypíše dve čísla, počet kráv a sliepok na farme, za ktorými nasledujú slová „kravy“ a „kurčatá“. K číslam vpredu musíte pridať nuly, aby každé obsadilo presne tri pozície.

007 Kravy 011 Kurčatá

Je zrejmé, že potrebujeme funkciu s dvoma argumentmi. Začnime kódovať.
// tlač funkcie FarmInventory printFarmInventory(kravy, kurčatá) ( var cowString = String(kravy); while (cowString.length< 3) cowString = "0" + cowString; console.log(cowString + " Коров"); var chickenString = String(chickens); while (chickenString.length < 3) chickenString = "0" + chickenString; console.log(chickenString + " Куриц"); } printFarmInventory(7, 11);

Ak k reťazcu pridáme .length, dostaneme jeho dĺžku. Ukazuje sa, že zatiaľ čo slučky pridávajte k číslam úvodné nuly, kým nezískate 3-znakový reťazec.

Pripravený! Ale práve keď sme sa chystali poslať kód farmárovi (samozrejme spolu s poriadnou kontrolou), zavolá a povie nám, že má na farme ošípané a mohli by sme pridať zobrazenie počtu ošípaných do program?

Samozrejme je to možné. Ale keď začneme kopírovať a vkladať kód z týchto štyroch riadkov, uvedomíme si, že sa musíme zastaviť a zamyslieť sa. Musí existovať lepší spôsob. Snažíme sa vylepšiť program:

// výstup S funkciou pridávania núl A štítkov printZeroPaddedWithLabel(číslo, štítok) ( var numberString = String(číslo); while (numberString.length< 3) numberString = "0" + numberString; console.log(numberString + " " + label); } // вывестиИнвентаризациюФермы function printFarmInventory(cows, chickens, pigs) { printZeroPaddedWithLabel(cows, "Коров"); printZeroPaddedWithLabel(chickens, "Куриц"); printZeroPaddedWithLabel(pigs, "Свиней"); } printFarmInventory(7, 11, 3);

Tvorba! Ale názov printZeroPaddedWithLabel je trochu zvláštny. Spája tri veci – výstup, pridávanie núl a označenie – do jednej funkcie. Namiesto vloženia celého opakujúceho sa fragmentu do funkcie poukážu na jeden koncept:

// pridanie nulovej funkcie zeroPad(číslo, šírka) ( var string = String(číslo); while (string.length< width) string = "0" + string; return string; } // вывестиИнвентаризациюФермы function printFarmInventory(cows, chickens, pigs) { console.log(zeroPad(cows, 3) + " Коров"); console.log(zeroPad(chickens, 3) + " Куриц"); console.log(zeroPad(pigs, 3) + " Свиней"); } printFarmInventory(7, 16, 3);

Funkcia s pekným a jasným názvom zeroPad uľahčuje pochopenie kódu. A dá sa použiť v mnohých situáciách, nielen v našom prípade. Napríklad na zobrazenie formátovaných tabuliek s číslami.

Aké inteligentné a všestranné by mali byť funkcie? Môžeme napísať buď jednoduchú funkciu, ktorá doplní číslo nulami až na tri pozície, alebo sofistikovanú funkciu všeobecný účel na formátovanie čísel, podporu zlomkov, záporných čísel, zarovnanie bodov, vyplnenie rôznymi znakmi atď.

Dobrým pravidlom je pridávať iba funkcie, o ktorých viete, že budú užitočné. Niekedy je lákavé vytvoriť všeobecné rámce pre každú malú potrebu. Odolajte mu. Nikdy nedokončíte prácu, skončíte pri písaní kopy kódu, ktorý nikto nepoužije.

Funkcie a vedľajšie účinky Funkcie možno zhruba rozdeliť na tie, ktoré sa volajú pre ich vedľajšie účinky, a na tie, ktoré sa volajú na získanie určitej hodnoty. Samozrejme je tiež možné tieto vlastnosti kombinovať v jednej funkcii.

Prvá pomocná funkcia v príklade farmy, printZeroPaddedWithLabel, sa volá, pretože má vedľajší efekt: vytlačí reťazec. Druhý, zeroPad, kvôli návratovej hodnote. A nie je náhoda, že druhá funkcia sa hodí častejšie ako prvá. Funkcie, ktoré vracajú hodnoty, sa ľahšie navzájom kombinujú ako funkcie, ktoré spôsobujú vedľajšie účinky.

Čistá funkcia je špeciálny druh funkcie vracajúcej hodnotu, ktorá nielenže nemá žiadne vedľajšie účinky, ale nezávisí ani od vedľajších účinkov zvyšku kódu – napríklad nepracuje s globálnymi premennými, ktoré by mohli byť náhodne zmenil niekde inde. Čistá funkcia, keď sa volá s rovnakými argumentmi, vráti rovnaký výsledok (a nerobí nič iné) – čo je celkom fajn. Ľahko sa s ňou pracuje. Volanie takejto funkcie môže byť mentálne nahradené výsledkom jej práce bez toho, aby sa zmenil význam kódu. Keď chcete takúto funkciu otestovať, môžete ju jednoducho zavolať a byť si istý, že ak funguje v danom kontexte, bude fungovať v akomkoľvek kontexte. Menej čisté funkcie môžu vrátiť rôzne výsledky v závislosti od mnohých faktorov a môžu mať vedľajšie účinky, ktoré sa ťažko testujú a zohľadňujú.

Nemali by ste sa však hanbiť písať funkcie, ktoré nie sú úplne čisté, alebo začať s posvätným čistením kódu od takýchto funkcií. Vedľajšie účinky sú často prospešné. Neexistuje spôsob, ako napísať čistú verziu funkcie console.log a táto funkcia je celkom užitočná. Niektoré operácie sa dajú ľahšie vyjadriť pomocou vedľajších účinkov.

Zhrnutie Táto kapitola vám ukázala, ako písať svoje vlastné funkcie. Keď sa kľúčové slovo funkcie použije ako výraz, vráti ukazovateľ na volanie funkcie. Pri použití ako inštrukcia môžete premennú deklarovať tak, že jej priradíte volanie funkcie.

Kľúčom k pochopeniu funkcií je lokálny rozsah. Parametre a premenné deklarované vo vnútri funkcie sú pre ňu lokálne, vytvárajú sa znova pri každom jej volaní a zvonku nie sú viditeľné. Funkcie deklarované v inej funkcii majú prístup k jej rozsahu.

Je veľmi užitočné rozdeliť rôzne úlohy vykonávané programom do funkcií. Nemusíte sa opakovať, funkcie robia kód čitateľnejším jeho rozdelením na zmysluplné časti, rovnako ako kapitoly a časti knihy pomáhajú organizovať bežný text.

CvičeniaMinimum V predchádzajúcej kapitole sme spomenuli funkciu Math.min, ktorá vracia najmenší zo svojich argumentov. Teraz môžeme takúto funkciu napísať sami. Napíšte funkciu min, ktorá vezme dva argumenty a vráti minimum z nich.

Console.log(min(0, 10)); // → 0 console.log(min(0, -10)); // → -10

Rekurzia Videli sme, že operátor % (modulo) možno použiť na určenie, či je číslo (%2) párne. Tu je ďalší spôsob, ako to definovať:

Nula je párna.
Jednotka je nepárna.
Akékoľvek číslo N má rovnakú paritu ako N-2.

Napíšte rekurzívnu funkciu jeDokonca podľa týchto pravidiel. Musí prijať číslo a vrátiť boolovskú hodnotu.

Otestujte to na 50 a 75. Skúste dať -1. Prečo sa takto správa? Dá sa to nejako opraviť?

Otestujte to na 50 a 75. Pozrite sa, ako sa správa na -1. prečo? Viete si predstaviť, ako to vyriešiť?

Console.log(isEven(50)); // → true console.log(isEven(75)); // → false console.log(isEven(-1)); // → ??

Počítanie fazule.

Číslo znaku N v reťazci možno získať pridaním .charAt(N) („reťazec“.charAt(5)) k nemu – podobným spôsobom ako pri získavaní dĺžky reťazca pomocou .length. Návratová hodnota bude reťazec pozostávajúci z jedného znaku (napríklad „k“). Prvý znak reťazca má pozíciu 0, čo znamená, že posledný znak bude mať pozíciu string.length - 1. Inými slovami, reťazec dvoch znakov má dĺžku 2 a jeho pozície znakov budú 0 a 1.

Napíšte funkciu countBs, ktorá vezme reťazec ako argument a vráti počet znakov „B“ obsiahnutých v reťazci.

Potom napíšte funkciu s názvom countChar, ktorá funguje niečo ako countBs, ale berie druhý parameter – znak, ktorý budeme hľadať v reťazci (namiesto toho, aby sme len počítali počet znakov „B“). Za týmto účelom prepracujte funkciu countBs.

Funkcie sú jedným z najdôležitejších stavebných kameňov kódu v JavaScripte.

Funkcie pozostávajú zo sady príkazov a zvyčajne vykonávajú jeden konkrétnu úlohu(napríklad sčítanie čísel, výpočet koreňov atď.).

Kód umiestnený vo funkcii sa vykoná až po výslovnom volaní tejto funkcie.

Vyhlásenie o funkcii

1. Syntax:

//Deklarácia funkcie functionFunctionname(ln1, ln2)( Kód funkcie) //Volanie funkcieFunctionname(ln1,lr2);

2. Syntax:

//Deklarácia funkcie var function name=function(ln1, ln2)(Kód funkcie) //Volanie funkcie name function name(ln1,lr2);

functionname určuje názov funkcie. Každá funkcia na stránke musí mať jedinečný názov. Názov funkcie musí byť zadaný latinkou a nesmie začínať číslicami.

ln1 a ln2 sú premenné alebo hodnoty, ktoré možno odovzdať do funkcie. Každej funkcii je možné odovzdať neobmedzený počet premenných.

Upozornenie: aj keď funkcii neodovzdáte žiadne premenné, nezabudnite za názov funkcie vložiť zátvorky „()“.

Upozorňujeme, že názvy funkcií v JavaScripte rozlišujú veľké a malé písmená.

Príklad funkcie JavaScript

Funkcia messageWrite() v príklade nižšie sa spustí až po kliknutí na tlačidlo.

Všimnite si, že tento príklad používa udalosť onclick. Udalosti JavaScript bude podrobne diskutované neskôr v tejto učebnici.

// Funkcia zapíše text do funkcie stránky messageWrite() ( document.write("Tento text bol na stránku napísaný pomocou JavaScriptu!"); )

Odovzdávanie premenných funkciám

Funkciám môžete odovzdať neobmedzený počet premenných.

Upozorňujeme: všetky manipulácie s premennými vo funkciách sa v skutočnosti nevykonávajú na samotných premenných, ale na ich kópii, takže obsah samotných premenných sa v dôsledku vykonávania funkcií nemení.

/* Definujme funkciu, ktorá pridá 10 k odovzdanej premennej a zobrazí výsledok na stránke */ function plus(a)( a=a+10; document.write("Výstup funkcie: " + a+"
"); ) var a=25; document.write("Hodnota premennej pred volaním funkcie: "+a+"
"); // Zavolajte funkciu tak, že jej odošlete premennú plus(a); document.write("Hodnota premennej po volaní funkcie: "+a+"
");

Rýchle zobrazenie

Ak chcete pristupovať ku globálnej premennej z funkcie a nie z jej kópie, použite window.variable_name.

Funkcia plus(a)( okno.a=a+10; ) var a=25; document.write("Hodnota premennej pred volaním funkcie: "+a+"
"); plus(a); document.write("Hodnota premennej po volaní funkcie: "+a+"
");

Rýchle zobrazenie

návratový príkaz

Pomocou príkazu return môžete vrátiť hodnoty z funkcií.

//Funkcia sum vráti súčet premenných, ktoré jej boli odovzdané function sum(v1,v2)( return v1+v2; ) document.write("5+6=" + sum(5,6) + "
"); document.write("10+4=" + suma(10,4) + "
");

Rýchle zobrazenie

Vstavané funkcie

Okrem užívateľsky definovaných funkcií má JavaScript aj vstavané funkcie.

Napríklad vstavaná funkcia isFinite vám umožňuje skontrolovať, či odovzdaná hodnota je platné číslo.

Document.write(isFinite(40)+"
"); document.write(isFinite(-590)+"
"); document.write(isFinite(90,33)+"
"); document.write(isFinite(NaN)+"
"); document.write(isFinite("Toto je reťazec")+"
");

Rýchle zobrazenie

Poznámka: úplný zoznam vstavaný JavaScript funkcie Nájdete ho v našom.

Lokálne a globálne premenné

Premenné vytvorené vo funkciách sa nazývajú lokálne premenné. K takýmto premenným máte prístup iba v rámci funkcií, v ktorých boli definované.

Po dokončení vykonávania kódu funkcie sa takéto premenné zničia. To znamená, že premenné s rovnakým názvom môžu byť definované v rôznych funkciách.

Premenné, ktoré sú vytvorené mimo funkčného kódu, sa nazývajú globálne premenné; k takýmto premenným je možné pristupovať odkiaľkoľvek v kóde.

Ak vo funkcii deklarujete premennú bez var, stane sa tiež globálnou.

Globálne premenné sa zničia až po zatvorení stránky.

//Deklarovanie globálnych premenných var1 a var2 var1="var1 existuje"; var var2; function func1() ( //Priraďte var2 hodnotu vnútri funkcie func1 var var2="var2 existuje"; ) //Z inej funkcie vypíšte obsah premennej var1 a var2 do funkcie stránky func2() ( //Tlač obsah premennej var1 document.write( var1 + "
"); //Vypíše obsah premennej var2 document.write(var2); )

Rýchle zobrazenie

Všimnite si, že po vytlačení na obrazovku bude mať var2 prázdnu hodnotu, pretože func1 funguje na lokálnej "verzii" var2.

Používanie anonymných funkcií

Funkcie, ktoré pri deklarácii neobsahujú názov, sa nazývajú anonymné.

Anonymné funkcie sú v zásade deklarované tak, že nie sú následne volané z kódu ako bežné funkcie, ale aby boli odovzdané iným funkciám ako parameter.

Funkcia arrMap(arr,func)( var res=nové pole; pre (var i=0;i