Deklarování funkce v sekci deklarace. Struktura programu. Výrazy a operátory v JavaScriptu
Ahoj všichni! V tomto článku chci mluvit o čem jako je deklarace funkce a výraz funkce v programovacím jazyce JavaScript.
Na webu už byl článek o tom, co je funkce, jak ji vytvořit a používat. Pokud jste ji ještě nečetli, můžete si ji přečíst
O tom se mluvilo v článku deklarace funkce.
Pokud se chcete zeptat funkční výraz, pak musíte napsat takto:
Var func = funkce(a, b) ( return a + b; );
Tito. proměnné přiřadíme funkci. Vezměte prosím na vědomí, že na konci je středník a to je nutné udělat vždy, protože toto je pouze hodnota proměnné in deklarace funkce Není potřeba používat středníky. Ve skutečnosti, pokud středník nevložíte, pak se s největší pravděpodobností nestane nic špatného, ale existují situace, kdy to z vás může udělat krutý vtip a pak bude docela obtížné tuto chybu najít.
Nyní si promluvme o rozdílech mezi těmito dvěma deklaracemi funkcí.
Deklarace funkce
součet();Funkce sum() (
alert("Fungovalo to!");
}
Takže pokud jste si mysleli, tady je správná odpověď: ANO, bude pracovat.
Jak je tohle možné?
Odpověď je velmi jednoduchá. Faktem je, že před spuštěním kódu jej interpret projde a shromáždí všechny deklarace proměnných a funkcí na speciálním místě. Ukáže se, že on, interpret, již zná celý zdrojový kód funkce a může ji volat, i když je v kódu napsána níže.
Funkční výraz
Všechno je zde přesně naopak.
Var func = function() (
alert("Fungovalo to!");
};
Interpret se o takových funkcích dozví, až když na ně přijde v kódu, takže výše uvedený příklad nebude fungovat. Pokud však níže napíšete volání funkce, pak bude vše fungovat.
Proč potřebujeme výraz funkce?
Ve skutečnosti se používají na mnoha místech, ale uvedu zde extrémně banální příklad.
If(věk >= 18) (
var func = function() (
alert("Vítejte!");
};
) jinak (
var func = function() (
alert("Na tebe je ještě příliš brzy!");
};
}
Nyní, v závislosti na podmínkách, do proměnné func bude napsán jiný zdrojový kód, a proto se funkce budou chovat odlišně.
S deklarace funkce takový trik nebude fungovat.
If(věk >= 18) (
funkce function() (
alert("Vítejte!");
}
) jinak (
funkce function() (
alert("Na tebe je ještě příliš brzy!");
}
}
Zde je pro vás další problém. Přemýšlejte o tom, co výše uvedený kód udělá a co upozornění(která funkce) bude nakonec fungovat.
Interpret se podívá na kód shora dolů, proto zapíše hodnotu poslední funkce a zavolá ji.
Doufám tedy, že chápete rozdíly mezi těmito 2 deklaracemi funkcí.
The funkce klíčové slovo lze použít k definování funkce uvnitř výrazu.
Zdroj pro tento interaktivní příklad je uložen v úložišti GitHub. Pokud byste chtěli přispět k projektu interaktivních příkladů, naklonujte prosím https://github.com/mdn/interactive-examples a zašlete nám žádost o stažení.
Syntax
var myFunction = funkce [ název]([param1[, param2[, ..., paramN]]]) { prohlášení };Parametry
name Název funkce. Lze vynechat, v takovém případě je funkce anonymní. Název je pouze lokální pro tělo funkce. paramN Název argumentu, který má být předán funkci. příkazy Příkazy, které tvoří tělo funkce.Popis
Výraz funkce je velmi podobný a má téměř stejnou syntaxi jako příkaz funkce (podrobnosti viz příkaz funkce). Hlavní rozdíl mezi výrazem funkce a příkazem funkce je název funkce které lze ve výrazech funkcí vynechat a vytvořit anonymní funkcí. Funkční výraz lze použít jako IIFE (Immediately Invoked Function Expression), který se spustí, jakmile je definován. Další informace naleznete také v kapitole o funkcích.
Funkce exprese zvedání
Funkční výrazy v JavaScriptu nejsou na rozdíl od deklarací funkcí zvednuty. Nemůžete použít funkční výrazy, než je definujete:
Console.log(notHoisted) // nedefinováno //i když je název proměnné zvednutý, definice není, takže je nedefinovaná. notHoisted(); // TypeError: notHoisted není funkce var notHoisted = function() ( console.log("bar"); );
Výraz pojmenované funkce
Pokud chcete odkazovat na aktuální funkci v těle funkce, musíte vytvořit výraz pojmenované funkce. Tento název je pak lokální pouze pro tělo funkce (rozsah) . Tím se také vyhnete použití nestandardní vlastnosti arguments.callee.
Var math = ( "factit": function factorial(n) ( console.log(n) if (n<= 1) { return 1; } return n * factorial(n - 1); } }; math.factit(3) //3;2;1;
Proměnná, ke které je funkční výraz přiřazen, bude mít vlastnost name. Název se nezmění, pokud je přiřazen k jiné proměnné. Pokud je název funkce vynechán, bude to název proměnné (implicitní název). Pokud je přítomen název funkce, bude to název funkce (explicitní název). To platí také pro funkce šipek (šipky nemají název, takže proměnné můžete dát pouze implicitní název).
Var foo = function() () foo.name // "foo" var foo2 = foo foo2.name // "foo" var bar = funkce baz() () bar.name // "baz" console.log(foo === foo2); // true console.log(typeof baz); // nedefinováno console.log(bar === baz); // false (chyby, protože baz == nedefinováno)
Příklady
Následující příklad definuje nepojmenovanou funkci a přiřadí ji x . Funkce vrátí druhou mocninu svého argumentu:
Var x = funkce(y) ( return y * y; ); button.addEventListener("click", function(event) ( console.log("tlačítko je kliknuto!") ))
Specifikace
Specifikace | Postavení | Komentář |
---|---|---|
ECMAScript nejnovější koncept (ECMA-262) |
Návrh | |
ECMAScript 2015 (6. vydání, ECMA-262) Definice "definice funkcí" v této specifikaci. |
Standard | |
ECMAScript 5.1 (ECMA-262) |
Standard | |
ECMAScript 3rd Edition (ECMA-262) Definice "definice funkce" v této specifikaci. |
Standard | Počáteční definice. Implementováno v JavaScriptu 1.5. |
Kompatibilita s prohlížečem
Tabulka kompatibility na této stránce je generována ze strukturovaných dat. Pokud byste chtěli přispět k datům, podívejte se na https://github.com/mdn/browser-compat-data a pošlete nám žádost o stažení.
Aktualizujte údaje o kompatibilitě na GitHubu
plocha počítače | mobilní, pohybliví | Server | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Chrome | Okraj | Firefox | internet Explorer | Opera | Safari | Webové zobrazení Android | Chrome pro Android | Firefox pro Android | Opera pro Android | Safari na iOS | Internet Samsung | Node.js | |
funkce | Plná podpora Chrome Ano | Plná podpora Edge 12 | Plná podpora Firefoxu 1 | IE Plná podpora Ano | Opera Plná podpora Ano | Safari Plná podpora Ano | WebView Plná podpora Androidu Ano | Chrome Plná podpora Androidu Ano | Firefox Plná podpora Androidu 4 | Opera Android Plná podpora Ano | Safari iOS Plná podpora Ano | Samsung Internet Android Plná podpora Ano | nodejs Plná podpora Ano |
Koncová čárka v parametrech | Plná podpora Chrome 58 | Edge No support No | Plná podpora Firefoxu 52 | IE Žádná podpora Ne | Opera plná podpora 45 | Safari Žádná podpora Ne | WebView Plná podpora Androidu 58 | Chrome Plná podpora Androidu 58 | Firefox Plná podpora Androidu 52 | Opera Android Plná podpora 43 | Safari iOS Žádná podpora Ne | Samsung Internet Android Plná podpora 7.0 | nodejs Plná podpora 8.0.0 |
Legenda
Plná podpora Plná podpora Bez podpory Bez podporyJako objekt jazyka C musí být funkce deklarována. Deklarování uživatelské funkce, tzn. jeho deklarace se provádí ve dvou formách - ve formě popisu a ve formě definice.
Popis funkce spočívá v tom, že nejprve představíte programový soubor jejího prototypu. Prototyp funkce informuje kompilátor, že jeho úplná definice (jeho plný text) bude uvedena později v textu programu: v aktuálním nebo jiném zdrojovém souboru nebo umístěná v knihovně.
Jazyková norma používá následující metodu deklarace funkcí:
typ_výsledku ID _funkce(typ proměnné1, ..., typ proměnné N);
Všimněte si, že není nutné uvádět identifikátory proměnných v závorkách prototypu, protože jazykový kompilátor je nezpracovává.
Popis prototypu umožňuje kompilátoru zkontrolovat, zda se typy a počet parametrů shodují, když je funkce skutečně volána.
Příklad zábavného popisu funkce se seznamem parametrů:
float fun(int, float, int, int);
Kompletní definice funkce je následující:
typ_výsledku ID _functions (seznam parametrů)
funkční kód
Typ výsledku určuje typ výrazu, jehož hodnota je vrácena do bodu jeho volání pomocí operátoru vrátit se<выражение> .
Pokud typ funkce není zadán, předpokládá se výchozí typ int .
Seznam parametrů se skládá ze seznamu typů parametrů a identifikátorů parametrů oddělených čárkami.
Funkce nemusí mít parametry, ale závorky jsou v každém případě povinné.
Pokud funkce nevrací žádnou hodnotu, musí být deklarována jako funkce typu prázdnota (prázdný).
V tomto případě operátor vrátit se nemusíš to dávat.
Funkce může mít více příkazů vrátit se , ale nemusí tam být žádné. V takových případech dojde k návratu k volajícímu programu po provedení posledního příkazu ve funkci.
Příklad funkce, která určuje nejmenší hodnotu dvou celočíselných proměnných:
int min (int x, int y)
návrat (x
Všechny funkce, které vracejí hodnotu, musí být použity na pravé straně výrazů C, jinak bude vrácený výsledek ztracen.
Pokud funkce nemá seznam parametrů, je vhodné při deklaraci takové funkce uvést v závorce i klíčové slovo void. Například, void main (void).
Výrazy v JavaScriptu jsou kombinace operandy A operátory.
Operace ve výrazech se provádějí postupně podle hodnoty priority (čím vyšší je hodnota priority, tím je vyšší). Vrácený výsledek není vždy stejného typu jako typ zpracovávaných dat. Například operace porovnání zahrnují operandy různých typů, ale výsledkem bude vždy booleovský typ.
Rýže. 1. Struktura výrazů v JavaScriptuOperandy jsou data zpracovaná skriptem JavaScript. Operandy mohou být jednoduché nebo složité datové typy, stejně jako další výrazy.
Operátoři jsou jazykové symboly, které provádějí různé operace s daty. Operátory lze psát pomocí interpunkčních znaků nebo klíčových slov.
V závislosti na počtu operandů se rozlišují následující typy operátorů:
unární— jeden operand je zapojen do operace;
binární— operace zahrnuje dva operandy;
trojice— kombinuje tři operandy.
Nejjednodušší forma vyjádření je doslovný— něco, co se samo hodnotí, například číslo 100, řetězec „Ahoj světe“ . Proměnná může být také výraz, protože se vyhodnocuje na hodnotu, která je jí přiřazena.
Výrazy a operátory v JavaScriptu
1. Aritmetické operátory
Aritmetické operátory jsou navrženy k provádění matematických operací, pracují s číselnými operandy (nebo proměnnými, které ukládají číselné hodnoty), a jako výsledek vracejí číselnou hodnotu.
Pokud je jedním z operandů řetězec, interpret JavaScriptu se jej pokusí převést na číselný typ a poté provede příslušnou operaci. Pokud převod typu není možný, výsledkem bude NaN (nikoli číslo).
Operátor/Provoz | Popis | Priorita |
---|---|---|
+ Dodatek | Přidá číselné operandy. Pokud je jedním z operandů řetězec, pak výsledkem výrazu je řetězec. | 12 |
- Odečítání | Odečte druhý operand od prvního. | 12 |
- Unární mínus | Převede kladné číslo na záporné a naopak. | 14 |
* Násobení | Vynásobí dva operandy. | 13 |
/ Divize | Vydělí první operand druhým. Výsledkem dělení může být celé číslo nebo číslo s plovoucí desetinnou čárkou. | 13 |
% Modulo dělení (zbytek divize) | Vypočítá zbytek vyplývající z celočíselného dělení prvního operandu druhým. Platí pro celá čísla i čísla s plovoucí desetinnou čárkou. | 13 |
2. Operátoři přiřazení
Operátoři přiřazení se používají k přiřazení hodnot proměnným. Kombinované operátory umožňují uložit původní a následné hodnoty do jedné proměnné.
var a = 5; // přiřadí číselnou hodnotu 5 proměnné a var b = "hello"; // ulož řetězec hellow do proměnné b var m = n = z = 10; // přiřadíme proměnným m, n, z číselnou hodnotu 10 x += 10; // ekvivalentní x = x + 10; x = 10; // ekvivalentní x = x - 10; x*= 10; // ekvivalentní x = x * 10; x/= 10; // ekvivalentní x = x / 10; x %= 10; // ekvivalentní x = x % 10;3. Operátory inkrementace a dekrementace
Operace zvýšení a snížení jsou unární a zvyšují a snižují hodnotu operandu o jedna. Operandem může být proměnná, prvek pole nebo vlastnost objektu. Nejčastěji se takové operace používají ke zvýšení čítače ve smyčce.
var x = y = m = n = 5, z, s, k, l; z = ++x* 2; /* jako výsledek výpočtů vrátí hodnotu z = 12, x = 6, tzn. hodnota x se nejprve zvýší o 1 a poté se provede operace násobení */ s = y++ * 2; /* jako výsledek výpočtů vrátí hodnotu s = 10, y = 6, tzn. Nejprve se provede operace násobení a poté se hodnota zvýšená o 1 uloží do proměnné y */ k = --m * 2; // vrátí hodnotu k = 8, m = 4 l = n-- * 2; // vrátí hodnotu l = 10, n = 44. Porovnávací operátory
Porovnávací operátory se používají ke shodě operandů, výsledkem výrazu může být jedna ze dvou hodnot - true nebo false . Operandy mohou být nejen čísla, ale také řetězce, logické hodnoty a objekty. Porovnání však lze provádět pouze na číslech a řetězcích, takže se převedou operandy, které nejsou čísly nebo řetězci.
Pokud oba operandy nelze úspěšně převést na čísla nebo řetězce, operátory vždy vrátí false .
Pokud jsou oba operandy řetězce/čísla nebo je lze převést na řetězce/čísla, budou porovnány jako řetězce/čísla.
Pokud je jeden operand řetězec/převádí se na řetězec a druhý je číslo/převádí se na číslo, operátor se pokusí převést řetězec na číslo a provést porovnání čísel. Pokud řetězec není číslo, převede se na NaN a výsledek porovnání je false .
Nejčastěji se srovnávací operace používají při organizování poboček v programech.
Operátor/Provoz | Popis | Priorita |
---|---|---|
== Rovnost | Testuje dvě hodnoty na stejnou hodnotu, což umožňuje převod typu. Vrátí hodnotu true, pokud jsou operandy stejné, a hodnotu false, pokud jsou různé. | 9 |
!= Nerovnost | Vrátí hodnotu true, pokud se operandy nerovnají | 9 |
=== Identita | Testuje dva operandy na "identitu" pomocí přesné definice shody. Vrátí hodnotu true, pokud jsou operandy stejné bez konverze typu. | 9 |
!== Netotožnost | Provádí ověření identity. Vrátí hodnotu true, pokud operandy nejsou stejné bez konverze typu. | 9 |
> Více | Vrátí hodnotu true, pokud je první operand větší než druhý, jinak vrátí hodnotu false. | 10 |
>= Větší nebo rovno | Vrátí hodnotu true, pokud první operand není menší než druhý, jinak vrátí hodnotu false. | 10 |
Vrátí hodnotu true, pokud je první operand menší než druhý, jinak vrátí hodnotu false. | 10 | |
Vrátí hodnotu true, pokud první operand není větší než druhý, jinak vrátí hodnotu false. | 10 |
5. Logické operátory
Logické operátory umožňují kombinovat podmínky, které vracejí booleovské hodnoty. Nejčastěji se používá v podmíněném příkazu if.
(2 < 3) && (3===3); // вернет true, так как выражения в обеих скобках дают true (x < 10 && x >0); // vrátí true, pokud je x v rozsahu od 0 do 10 !false; // vrátí true6. Bitové operátory
Bitové operátory pracovat s operandy jako 32bitovou sekvencí jedniček a nul a vracet číselnou hodnotu označující výsledek operace zapsanou v desítkové soustavě. Celá čísla jsou považována za operandy, zlomková část operandu se zahodí. Bitové operace lze využít například při šifrování dat, práci s příznaky a vymezování přístupových práv.
Operátor/Provoz | Popis | Priorita |
---|---|---|
& Bitově AND | Pokud jsou oba bity 1, bude výsledný bit 1. Jinak je výsledek 0. | 8 |
| Bitově NEBO | Pokud jeden z operandů obsahuje 1 na pozici, bude výsledek obsahovat také 1 na této pozici, jinak bude výsledek na této pozici 0. | 6 |
^ Exkluzivní NEBO | Pokud jedna a pouze jedna hodnota obsahuje na libovolné pozici 1, bude výsledek obsahovat na této pozici 1, jinak bude výsledek na této pozici 0. | 7 |
~ Popírání | Na binární reprezentaci hodnoty výrazu se provede bitová negační operace. Jakákoli pozice obsahující 1 v původním výrazu je nahrazena 0. Jakákoli pozice obsahující 0 v původním výrazu se stane 0 . Kladná čísla začínají na 0, záporná čísla začínají na -1, takže ~ n == -(n+1) . | 14 |
Operátor posouvá bity prvního operandu doleva o počet bitových pozic nastavených druhým operandem. Nuly se používají k vyplnění pozic vpravo. Vrátí výsledek stejného typu jako levý operand. | 11 | |
>> Bitový posun doprava | Operátor posouvá bity prvního operandu doprava o počet bitových pozic nastavených druhým operandem. Číslice posunuté mimo rozsah jsou odstraněny. Nejvýznamnější bit (32.) je ponechán beze změny, aby se zachovalo znaménko výsledku. Pokud je první operand kladný, jsou nejvýznamnější bity výsledku vyplněny nulami; pokud je první operand záporný, jsou nejvýznamnější bity výsledku vyplněny jedničkami. Posunutí hodnoty doprava o jednu pozici je ekvivalentní dělení 2 (zahození zbytku) a posunutí doprava o dvě pozice je ekvivalentní dělení 4 atd. | 11 |
>>> Bitový posun doprava bez znaménka | Operátor posouvá bity prvního operandu doprava o počet bitových pozic nastavených druhým operandem. Nuly se přidávají vlevo, bez ohledu na znaménko prvního operandu. Číslice posunuté mimo rozsah jsou odstraněny. | 11 |
7. Řetězcové operátory
Existuje několik operátorů, které pracují s řetězci speciálními způsoby.
"1" + "10"; // návrat "110" "1" + 10; // vrátí "110" 2 + 5 + "barevné tužky"; // vrátí "7 barevných tužek" "Barevné tužky" + 2 + 5; // vrátí "25 barevných tužek" "1" > "10"; // vrátí false "10"<= 10; // вернет true "СССР" == "ссср"; // вернет false x = "micro"; x+= "soft"; // вернет "microsoft"8. Speciální operátory
Operátor/Provoz | Popis | Priorita |
---|---|---|
. Přístup k nemovitosti | Přistupuje k vlastnosti objektu. | 15 |
,Vícenásobný výpočet | Vyhodnocuje více nezávislých výrazů napsaných na jednom řádku. | 1 |
Indexování pole | Přistupuje k prvkům pole nebo vlastnostem objektu. | 15 |
() Volání funkce, seskupování | Seskupuje operace nebo volá funkci. | 15 |
typeof Definice datového typu | Unární operátor, vrací datový typ operandu. | 14 |
instanceof Kontrola typu objektu | Operátor kontroluje, zda je objekt instancí určité třídy. Levý operand musí být objekt, pravý operand musí obsahovat název třídy objektu. Výsledek bude pravdivý, pokud je objekt nalevo instancí třídy napravo, jinak bude nepravda. | 10 |
v Kontrola přítomnosti nemovitosti | Levý operand musí být řetězec a pravý operand musí být pole nebo objekt. Pokud je levá hodnota vlastností objektu, výsledek bude true . | 10 |
nový Vytvoření objektu | Operátor vytvoří nový objekt s nedefinovanými vlastnostmi, poté zavolá funkci konstruktoru, aby jej inicializoval (předání parametrů). Lze také použít k vytvoření pole. | 1 |
smazat Vymazat | Operátor umožňuje odstranit vlastnost z objektu nebo prvku z pole. Vrací hodnotu true, pokud bylo odstranění úspěšné, v opačném případě vrací hodnotu false. Když je prvek pole odstraněn, jeho délka se nezmění. | 14 |
void Definování výrazu bez návratové hodnoty | Unární operátor, zahodí hodnotu operandu a vrátí underfined . | 14 |
?: Operátor podmíněného výrazu | Ternární operátor umožňuje organizovat jednoduché větvení. Výraz obsahuje tři operandy, první musí být booleovská hodnota nebo musí být převedena na jednu a druhý a třetí musí být libovolné hodnoty. Pokud má první operand hodnotu true , pak podmíněný výraz převezme hodnotu druhého operandu; pokud nepravda - pak třetí. | 3 |
9. Komentáře v JavaScriptu
Jednořádkový komentář: před text komentáře musí být uvedeny symboly // .
Funkce jsou jedním ze základních stavebních kamenů JavaScriptu. Funkce je procedura JavaScriptu – sada příkazů, která provádí úlohu nebo vypočítává hodnotu. Chcete-li použít funkci, musíte ji definovat někde v rozsahu, ze kterého ji chcete volat.
A metoda je funkce, která je vlastností objektu. Přečtěte si více o objektech a metodách v části Práce s objekty.
Volání funkcí
Definování funkce ji neprovede. Definování funkce jednoduše pojmenuje funkci a určí, co se má dělat, když je funkce volána. Povolání funkce skutečně provádí zadané akce s uvedenými parametry. Pokud například definujete funkci square , můžete ji nazvat takto:
Čtverec(5);
Předchozí příkaz volá funkci s argumentem 5. Funkce provede své příkazy a vrátí hodnotu 25.
Funkce musí být v rozsahu, když jsou volány, ale deklaraci funkce lze zvednout (objeví se pod voláním v kódu), jako v tomto příkladu:
Console.log(čtverec(5)); /* ... */ function square(n) ( return n * n; )
Rozsah funkce je funkce, ve které je deklarována, nebo celý program, pokud je deklarován na nejvyšší úrovni.
Poznámka: To funguje pouze při definování funkce pomocí výše uvedené syntaxe (tj. funkce funcName()()). Níže uvedený kód nebude fungovat. To znamená, že funkce zvedání funguje pouze s deklarací funkce a ne s výrazem funkce.
Console.log(čtverec); // čtverec je zvednut s počáteční hodnotou nedefinovanou. console.log(čtverec(5)); // Uncaught TypeError: square není funkce var square = function(n) ( return n * n; )
Argumenty funkce nejsou omezeny na řetězce a čísla. Funkci můžete předat celé objekty. Funkce show_props() (definovaná v ) je příkladem funkce, která přebírá objekt jako argument.
Funkce může volat sama sebe. Zde je například funkce, která rekurzivně počítá faktoriály:
Funkce faktoriál(n) ( if ((n === 0) || (n === 1)) návrat 1; jinak návrat (n * faktoriál(n - 1)); )
Potom byste mohli vypočítat faktoriály jedna až pět takto:
Var a, b, c, d, e; a = faktoriál(1); // a získá hodnotu 1 b = faktoriál(2); // b získá hodnotu 2 c = faktoriál(3); // c získá hodnotu 6 d = faktoriál(4); // d dostane hodnotu 24 e = faktoriál(5); //e získá hodnotu 120
Existují i jiné způsoby volání funkcí. Často existují případy, kdy je třeba funkci volat dynamicky nebo se počet argumentů funkce liší, nebo kdy je třeba kontext volání funkce nastavit na konkrétní objekt určený za běhu. Ukazuje se, že funkce jsou samy o sobě objekty a tyto objekty zase mají metody (viz objekt Function). K dosažení tohoto cíle lze použít jednu z nich, metodu apply().
Rozsah funkcí
K proměnným definovaným uvnitř funkce nelze přistupovat odkudkoli mimo funkci, protože proměnná je definována pouze v rozsahu funkce. Funkce však může přistupovat ke všem proměnným a funkcím definovaným v rozsahu, ve kterém je definována. Jinými slovy, funkce definovaná v globálním rozsahu může přistupovat ke všem proměnným definovaným v globálním rozsahu. Funkce definovaná uvnitř jiné funkce může také přistupovat ke všem proměnným definovaným v její rodičovské funkci a jakékoli jiné proměnné, ke které má rodičovská funkce přístup.
// Následující proměnné jsou definovány v globálním rozsahu var num1 = 20, num2 = 3, name = "Chamahk"; // Tato funkce je definována ve funkci globálního rozsahu multiply() ( return num1 * num2; ) multiply(); // Vrátí 60 // Příklad vnořené funkce funkce getScore() ( var num1 = 2, num2 = 3; funkce add() ( návratový název + " skóre " + (num1 + num2); ) return add(); ) getScore (); // Vrátí "Chamahk skóroval 5"
Rozsah a zásobník funkcí
Rekurze
Funkce může odkazovat a volat sama sebe. Funkce se může odkazovat na sebe třemi způsoby:
- název funkce
- proměnná v rozsahu, která odkazuje na funkci
Zvažte například následující definici funkce:
Var foo = function bar() ( // příkazy jdou sem );
V těle funkce jsou všechny následující ekvivalenty:
- bar()
- arguments.callee()
- foo()
Funkce, která volá sama sebe, se nazývá a rekurzivní funkce. V některých ohledech je rekurze analogická smyčce. Oba spouštějí stejný kód vícekrát a oba vyžadují podmínku (aby se zabránilo nekonečné smyčce, nebo v tomto případě spíše nekonečné rekurzi). Například následující smyčka:
Var x = 0; zatímco (x< 10) { // "x < 10" is the loop condition // do stuff x++; }
lze převést na rekurzivní funkci a volání této funkce:
Funkční smyčka(x) ( if (x >= 10) // "x >= 10" je výstupní podmínka (ekvivalentní "!(x< 10)") return; // do stuff loop(x + 1); // the recursive call } loop(0);
Některé algoritmy však nemohou být jednoduchými iteračními smyčkami. Například získání všech uzlů stromové struktury (např. DOM) se snadněji provede pomocí rekurze:
Funkce walkTree(node) ( if (node == null) // návrat; // udělejte něco s uzlem pro (var i = 0; i< node.childNodes.length; i++) { walkTree(node.childNodes[i]); } }
Ve srovnání s funkční smyčkou zde každé rekurzivní volání samo o sobě provádí mnoho rekurzivních volání.
Je možné převést jakýkoli rekurzivní algoritmus na nerekurzivní, ale často je logika mnohem složitější a vyžaduje použití zásobníku. Ve skutečnosti samotná rekurze používá zásobník: zásobník funkcí.
Chování podobné zásobníku lze vidět v následujícím příkladu:
Funkce foo(i) (pokud (i< 0) return; console.log("begin: " + i); foo(i - 1); console.log("end: " + i); } foo(3); // Output: // begin: 3 // begin: 2 // begin: 1 // begin: 0 // end: 0 // end: 1 // end: 2 // end: 3
Vnořené funkce a uzávěry
Funkci můžete vnořit do funkce. Vnořená (vnitřní) funkce je soukromá pro její obsahující (vnější) funkci. Tvoří také a uzavření. Uzávěr je výraz (obvykle funkce), který může mít volné proměnné spolu s prostředím, které tyto proměnné váže (které výraz „uzavře“).
Protože vnořená funkce je uzávěr, znamená to, že vnořená funkce může „zdědit“ argumenty a proměnné své obsahující funkce. Jinými slovy, vnitřní funkce obsahuje rozsah vnější funkce.
- Vnitřní funkce je přístupná pouze z příkazů ve vnější funkci.
- Vnitřní funkce tvoří uzávěr: vnitřní funkce může používat argumenty a proměnné vnější funkce, zatímco vnější funkce nemůže používat argumenty a proměnné vnitřní funkce.
Následující příklad ukazuje vnořené funkce:
Funkce addSquares(a, b) ( function square(x) ( return x * x; ) return square(a) + square(b); ) a = addSquares(2, 3); // vrátí 13 b = addSquares(3, 4); // vrátí 25 c = addSquares(4, 5); // vrátí 41
Protože vnitřní funkce tvoří uzávěr, můžete volat vnější funkci a zadat argumenty pro vnější i vnitřní funkci:
Funkce vně (x) ( funkce inside(y) ( return x + y; ) return inside; ) fn_inside = outside(3); // Přemýšlejte o tom jako: dejte mi funkci, která přidá 3 ke všemu, co dáte // it result = fn_inside(5); // vrátí 8 vysledek1 = mimo(3)(5); // vrátí 8
Zachování proměnných
Všimněte si, jak je x zachováno, když je vráceno dovnitř. Uzavření musí zachovat argumenty a proměnné ve všech oblastech, na které odkazuje. Vzhledem k tomu, že každé volání poskytuje potenciálně odlišné argumenty, vytvoří se pro každé volání mimo . Paměť lze uvolnit pouze tehdy, když vrácený vnitřek již není přístupný.
To se neliší od ukládání referencí do jiných objektů, ale často je to méně zřejmé, protože reference nenastavujeme přímo a nelze je kontrolovat.
Vícenásobně vnořené funkce
Funkce mohou být vícenásobně vnořené, tzn. funkce (A) obsahující funkci (B) obsahující funkci (C). Obě funkce B a C zde tvoří uzávěry, takže B má přístup k A a C má přístup k B. Navíc, protože C má přístup k B, které má přístup k A, C může také přistupovat k A. Uzávěry tedy mohou obsahovat více rozsahů; rekurzivně obsahují rozsah funkcí, které je obsahují. Tomu se říká řetězení rozsahu. (Proč se tomu říká „řetězení“ bude vysvětleno později.)
Zvažte následující příklad:
Funkce A(x) ( funkce B(y) ( funkce C(z) ( console.log(x + y + z); ) C(3); ) B(2); ) A(1); // protokoly 6 (1 + 2 + 3)
V tomto příkladu C přistupuje k B "s y a A "s x . To lze provést, protože:
- B tvoří uzávěr obsahující A, tzn. B má přístup k argumentům a proměnným A.
- C tvoří uzávěr včetně B.
- Protože uzávěr B zahrnuje A, uzávěr C zahrnuje A, C má přístup k oběma B a Argumenty a proměnné A. Jinými slovy, C řetězy rozsahy B a A v tomto pořadí.
Opak však není pravdou. A nemůže přistupovat k C , protože A nemá přístup k žádnému argumentu nebo proměnné B , což je C proměnná. C tedy zůstává soukromý pouze pro B .
Konflikty jmen
Když dva argumenty nebo proměnné v rozsahu uzávěru mají stejný název, existuje a konflikt jmen. Více vnitřních rozsahů má přednost, takže nejvnitřnější rozsah má nejvyšší prioritu, zatímco vnější rozsah má nejnižší. Toto je rozsahový řetězec. První na řetězu je nejvnitřnější dalekohled a poslední je nejvzdálenější. Zvažte následující:
Funkce outside() ( var x = 5; funkce inside(x) ( return x * 2; ) return inside; ) outside())(10); // vrátí 20 místo 10
Ke konfliktu názvů dochází při příkazu return x a nachází se mezi uvnitř "s parametrem x a vně "s proměnnou x. Řetězec rozsahu je zde ( inside , outside , global object). Proto uvnitř "s x má přednost před vnějším "s x" a vrátí se 20 (uvnitř "s x) místo 10 (mimo "s x).
Uzávěry
Uzávěry jsou jednou z nejvýkonnějších funkcí JavaScriptu. JavaScript umožňuje vnořování funkcí a poskytuje vnitřní funkci plný přístup ke všem proměnným a funkcím definovaným uvnitř vnější funkce (a všem ostatním proměnným a funkcím, ke kterým má vnější funkce přístup). Vnější funkce však nemá přístup k proměnným a funkcím definovaným uvnitř vnitřní funkce. To poskytuje jakési zapouzdření proměnných vnitřní funkce. Jelikož má vnitřní funkce přístup k rozsahu vnější funkce, proměnné a funkce definované ve vnější funkci budou žít déle, než je doba provádění vnější funkce, pokud se vnitřní funkci podaří přežít po dobu životnosti vnější funkce. funkce. Uzávěr se vytvoří, když je vnitřní funkce nějakým způsobem zpřístupněna libovolnému rozsahu mimo vnější funkci.
Var pet = function(name) ( // Vnější funkce definuje proměnnou s názvem "name" var getName = function() ( return name; // Vnitřní funkce má přístup k proměnné "name" vnější //function ) return getName; // Vrátí vnitřní funkci, čímž ji vystaví vnějším rozsahům ) myPet = pet("Vivie"); můj mazlíček(); // Vrátí "Vivie"
Může být mnohem složitější než výše uvedený kód. Lze vrátit objekt obsahující metody pro manipulaci s vnitřními proměnnými vnější funkce.
Var createPet = function(name) ( var sex; return ( setName: function(newName) ( name = newName; ), getName: function() ( return name; ), getSex: function() ( return sex; ), setSex: function(newSex) ( if(typeof newSex === "string" && (newSex.toLowerCase() === "male" || newSex.toLowerCase() === "female")) ( sex = newSex; ) ) ) ) var pet = createPet("Vivie"); pet.getName(); // Vivie pet.setName("Oliver"); pet.setSex("muž"); pet.getSex(); // samec pet.getName(); // Olivere
Ve výše uvedeném kódu je jmenná proměnná vnější funkce přístupná vnitřním funkcím a neexistuje žádný jiný způsob přístupu k vnitřním proměnným než prostřednictvím vnitřních funkcí. Vnitřní proměnné vnitřních funkcí fungují jako bezpečné úložiště pro vnější argumenty a proměnné. Obsahují „trvalá“ a „zapouzdřená“ data, se kterými mohou vnitřní funkce pracovat. Funkce ani nemusí být přiřazeny k proměnné nebo mít název.
Var getCode = (function() ( var apiCode = "0]Eal(eh&2"; // Kód, který nechceme, aby ho mohli upravovat lidé zvenčí... return function() ( return apiCode; ); ))() getCode(); // Vrátí apiCode
Existuje však řada úskalí, na která je třeba při používání uzávěrů dávat pozor. Pokud uzavřená funkce definuje proměnnou se stejným názvem jako název proměnné ve vnějším rozsahu, neexistuje způsob, jak znovu odkazovat na proměnnou ve vnějším rozsahu.
Var createPet = function(name) ( // Vnější funkce definuje proměnnou s názvem "name". return ( setName: function(name) ( // Přiložená funkce také definuje proměnnou s názvem "name". name = name; // Jak získáme přístup k "jménu" definovanému vnější funkcí? ) ) )
Použití objektu arguments
Argumenty funkce jsou udržovány v objektu podobném poli. V rámci funkce můžete adresovat argumenty, které jí byly předány, následovně:
argumenty[i]
kde i je pořadové číslo argumentu počínaje nulou. První argument předaný funkci by tedy byly argumenty . Celkový počet argumentů je označen arguments.length .
Pomocí objektu arguments můžete volat funkci s více argumenty, než je formálně deklarováno, že přijímá. To je často užitečné, pokud předem nevíte, kolik argumentů bude funkci předáno. Pomocí arguments.length můžete určit počet argumentů skutečně předávaných funkci a poté přistupovat ke každému argumentu pomocí objektu arguments.
Uvažujme například funkci, která zřetězí několik řetězců. Jediným formálním argumentem funkce je řetězec, který určuje znaky, které oddělují položky, které mají být zřetězeny. Funkce je definována následovně:
Funkce myConcat(separator) ( var result = ""; // inicializace seznamu var i; // iterace přes argumenty pro (i = 1; i< arguments.length; i++) { result += arguments[i] + separator; } return result; }
Této funkci můžete předat libovolný počet argumentů a zřetězí každý argument do řetězce „seznam“:
// vrátí "red, orange, blue, " myConcat(", ", "red", "orange", "blue"); // vrátí "slon; žirafa; lev; gepard; " myConcat("; ", "slon", "žirafa", "lev", "gepard"); // vrátí "šalvěj. bazalka. oregano. pepř. petržel. " myConcat(". ", "šalvěj", "bazalka", "oregano", "pepř", "petržel");
Poznámka: Proměnná arguments je "jako pole", ale ne pole. Je podobný poli v tom, že má očíslovaný index a vlastnost length. Nemá však všechny metody manipulace s polem.
Zavedení funkcí šipek ovlivnily dva faktory: kratší funkce a jejich nezávaznost.
Kratší funkce
V některých funkčních vzorech jsou kratší funkce vítány. Porovnat:
Var a = [ "Vodík", "Helium", "Lithium", "Beryllium" ]; var a2 = a.map(funkce(y) ( return s.length; )); console.log(a2); // protokoly var a3 = a.map(s => s.length); console.log(a3); // protokoly
Žádné oddělené
Dokud nefungovala šipka, každá nová funkce definovala svou vlastní hodnotu (nový objekt v případě konstruktoru, nedefinovaný ve volání funkcí, základní objekt, pokud je funkce volána jako "objektová metoda" atd.). To se ukázalo jako méně než ideální s objektově orientovaným stylem programování.
Funkce Person() ( // Konstruktor Person() definuje `toto` jako sebe samého. this.age = 0; setInterval(funkce growUp() ( // V nonstrict režimu funkce growUp() definuje `toto` // jako globální objekt, který se liší od `this` // definovaného konstruktorem Person() this.age++; ), 1000); ) var p = new Person();
V ECMAScript 3/5 byl tento problém vyřešen přiřazením hodnoty v tomto proměnné, kterou lze uzavřít.
Function Person() ( var self = this; // Někteří volí `to` místo `self`. // Vyberte si jednu a buďte konzistentní. self.age = 0; setInterval(function growUp() ( // Zpětné volání odkazuje na proměnná `self`, jejíž // hodnota je očekávaný objekt. self.age++; ), 1000); )