Uzáver JS. Okruhy v Javascript: Praktický príklad, funkcie a pravidlá

Ahojte všetci! V tomto článku sa pozrieme Čo je uzáver v Javascript.

Je to celková jednoduchá téma, ale vyžaduje to pochopenie. Po prvé, pozrime sa na to, čo sa deje vo vnútri funkcie.

Funkčný pozdrav (názov) (
// LexicalenNvironment \u003d (Názov: "Nikolai", text: Undefined)
var text \u003d "ahoj," + názov;
// lexicallenvironment \u003d (Názov: "Nikolai", text: "Ahoj, Nikolai")
Upozornenie (text);
}

Pozdrav ("Nikolai");

Čo sa tu deje a čo je Lexicallinionment? Poďme na to.

Keď je funkcia volaná, vytvára objekt LexicallinionmentV ktorom sú zaznamenané všetky lokálne premenné a funkcie, ako aj odkaz na externý rozsah (o tom neskôr). V našom prípade máme lokálnu premennú názov.ktorý má okamžite hodnotu (potom, čo prejdeme) a toto "Nikolai". V jednom z článkov, ktoré som už napísal, však pripomínam, že thlásenie vie všetko o všetkých premenných vopred. Je to o tom, že na samom začiatku funkcie už má premennú text., tlmočník o tom vie, ale keďže sme ešte nedosiahli pridelenie tejto premennej nejakého druhu, je rovná nedefinované.. Teraz priradíme variabilnú hodnotu a náš objekt Lexicallinionment Zmeny. Jeho majetok text. Zostáva rovná skutočnosti, že sme zaznamenali ("Ahoj, Nikolai" v našom prípade). Po vypracovaní funkcie objekt Lexicallinionment Zničiť. S nasledujúcimi hovormi bude znova vytvorený atď.

Teraz sa obrátime na ďalší príklad. Povedz mi, čo bude v tomto prípade odvodené?

Var b \u003d 2;
Funkcia X (A) (
ALERT (A + B);
}
x (1);

Myslel? Myslím si, že najviac odpovedal, že sa zobrazí číslo 3, a to je správna odpoveď, ale môžete vám povedať, ako sa tlmočník dozvedel o premennej b.? Koniec koncov, nie je v tele funkcie. Ak nie, povedzme.

V skutočnosti javascript. Tam je skrytá vlastnosť [] . Keď je funkcia deklarovaná, niekde je vždy deklarovaná. Táto funkcia môže byť v inej funkcii, môže byť v globálnom objekte atď. V našom prípade je funkcia deklarovaná v globálnom objekte. okno., Preto, majetok x. [] \u003d okno.

Var b \u003d 2;
Funkcia X (A) (// x. [] \u003d Okno
// lexicallenvironment \u003d (a: 1) -\u003e okno
ALERT (A + B);
}
x (1);

Táto šípka má objekt Lexicallinionment - Toto je odkaz na externý rozsah a tento odkaz je vytvorený podľa majetku. [] . Takže v objekte Lexicallinionment Budeme mať odkaz na externý objekt. okno.. Keď tlmočník hľadá premennú, potom ho najprv vyhľadá v objekte Lexicallinionment, potom, ak nenašiel premennú, potom sa pozrie do odkazu, ide do externého rozsahu a vyhľadáva ho tam a tak až do konca. Ak nikto nenašiel túto premennú, bude to chyba. V našom prípade premenlivá a. Tlmočník bude trvať z objektu Lexicallinionmenta premenná b. Z objektu okno.. Samozrejme, ak máme miestnu premennú b. S určitou hodnotou sa zaznamenáva v objekte Lexicallinionment A následne sa odtiaľ odoberú, a nie z vonkajšej oblasti viditeľnosti.

DÔLEŽITÉ! Pamätajte si, že majetok [] Je inštalovaný podľa miesta, kde bola funkcia deklarovaná, a nie je spôsobená, preto kód nižšie zobrazí číslo 3, a nie 5, ako si môžu myslieť.

BAR B \u003d 2;
Funkcia X (A) (
ALERT (A + B);
}

Funkcia Y () (
var b \u003d 4;
x (1);
}

Bolo to všetko prelude len na pochopenie toho, ako to všetko funguje, a to bolo pre vás jednoduchšie pochopiť, ako fungujú uzávery. A teraz sa obrátime priamo na tému článku.

Ako som povedal, objekt Lexicallinionment Zakaždým, keď je po vykonaní funkcie znova zničený a znova sa vytvorí pri opätovnom volaní. Čo však chceme uložiť tieto údaje? Tí. Chceme, aby bolo všetko zaznamenané Lexicallinionment Teraz sa zachoval a bol použitý pre tieto výzvy? Je to pre to uzáver.

Funkčný pozdrav (názov) (
// lexicallenvironment \u003d (Názov: "Nikolai")
Funkcia návratu () (// [] \u003d LexicalLironment
Upozornenie (meno);
};
}

Var func \u003d pozdrav ("Nikolai");
pozdrav \u003d null;
Func ();

Pozrime sa, čo sme urobili. Najprv vytvoríme funkciu pozdrav.v ktorom sa názov prenesie. Funkcia vytvorí objekt Lexicallinionmentkde je vlastnosť vytvorená (naša lokálna premenná) názov. A je pridelená názvu "Nikolai". A teraz dôležité: Vrátime inú funkciu z funkcie, vo vnútri, ktorý odvodzujeme upozornenie. premenlivý názov.. Ďalej priradíme premennú func. Hodnota vrátená z funkcie pozdrav.A táto hodnota je naša funkcia, ktorá zobrazuje názov. teraz pozdrav. Pridelíme nULOVÝ. Zničujeme našu funkciu pozdrav., Avšak, keď zavoláme func.Potom uvidíme hodnotu premennej názov.("Nikolai") funkcie pozdrav.. Ako vám je možné povedať? A veľmi jednoduché. To je, že naša vrátená funkcia má tiež nehnuteľnosť. [] ktorý odkazuje na vonkajší rozsah viditeľnosti a tento externý rozsah v našom prípade je objekt Lexicallinionment Naša funkcia pozdrav.. Preto, napriek tomu, že odstránime našu funkciu pozdrav., objekt Lexicallinionment Nie je odstránený a zostal v pamäti a zostane v pamäti, kým nie je aspoň jeden odkaz. Máme tento odkaz - naše vrátené funkcie, ktorá používa premennú názov. tohto zariadenia Lexicallinionment.

Poďme teraz dať definíciu Čo je uzavretie.

Okruh - Funkcia so všetkými premennými, ktoré sú k dispozícii.

No, článok sa ukázal dosť objemný, ale to je len preto, že som sa snažil opísať celý proces práce uzavretia. Na konsolidáciu, chcem priniesť jednoduchý príklad - meter pomocou témy práve študoval. Zaoberáte sa kódom a napíšte v komentároch, ako a prečo funguje. Ak niečo nerozumiete, môžete tiež položiť otázku. Ďakujem za pozornosť!

Funkcia MakeCounter () (
vartecount \u003d 0;

Funkcia návratu () (
Aktuálnecount ++;
Návrat;
};
}

Var counter \u003d makeCounter ();
Pult ();
Pult ();
Upozornenie (počítadlo ()); // 3.

V Funkcie Javascript Možno opísať nielen jeden po druhom, ale aj jeden vnútri druhého. Keď máte jednu funkciu, je vnútorná, vnútorná librácia má prístup k striedavej externej funkcii.

FUNKCIA VONKAJŠIE (X) (var TMP \u003d 3; Funkčné vnútorné (Y) (Upozornenie (X + Y + (++ TMP)); // Odstráni 16) Vnútorné (10);) VONKAJŠIE (2);

Tento kód vždy dáva 16, pretože vnútorná funkcia vidí x, čo je premenná vo funkcii Funkuction. V tento prípad Funkcia argumentu. Tiež interné () môže vidieť TMP z externého ().

Toto sa nazýva uzavretie alebo uzavretie. Ak je presnejšie, zatvorenie sa nazýva externá funkcia a všetko, v ktorom sa nachádza, sa nazýva prostredie uzáveru alebo uzáveru.

Niekedy sa hovorí, že uzáver je funkcia, ktorá vráti funkciu, je nesprávne, aby sa pomenoval funkciu uzavretia, že vnútorná funkcia sa vzťahuje na premennú z vonkajšej strany jej rozsahu.

Funkcia FOO (X) (var TMP \u003d 3; Vrátiť funkciu (Y) (Upozornenie (X + Y + (++ TMP)); // Bude tiež upozornenie 16)) var Bar \u003d FOO (2); // bar je teraz uzavretím. Bar (10);

Vyššie uvedená funkcia sa zobrazí aj 16, pretože bar dokonca aj po dokončení foo naďalej má prístup k X a TMP, aj keď samotná barná premenná nie je vo vnútri rozsahu, v akom boli vyhlásené.

Zároveň, pretože premenná TMP je stále vo vnútri uzatvárania pruhov, naďalej zvyšuje každý časový hovor.

Tu najjednoduchší príklad Okruh:

Var a \u003d 10; Funkčný test () (Console.Log (A); // Záver 10 Console.log (B); // Záver 6) var b \u003d 6; test ();

Keď spustíte funkciu v Javascriptovi, prostredie je pre ňu vytvorené, to znamená, že zoznam všetkých variabilných viditeľných, nielen argumenty a premenné to vyhlásili v rámci nej, ale aj vonku, v tomto príklade je to "a "A" B ".

V jednom prostredí môžete vytvoriť viac ako jedno uzávierky, čo ich vracia do poľa, objektu alebo pripevnenia na globálnu premennú. V tomto prípade budú všetci pracovať ten istý X alebo hodnota TMP bez vytvorenia jednotlivých kópií.

Vzhľadom k tomu, v našom príklade X je číslo, potom jeho hodnota kópia V foo ako jeho argument x.

Na druhej strane, JavaScript vždy používa odkazy, keď sa prenášajú objekty. Ak ste volali foo s objektom ako argument, potom sa spiatočná uzávierka vráti odkaz na pôvodný objekt!

Funkcia FOO (X) (var TMP \u003d 3; RETURN FUNCTION (Y) (ALERT (X + Y + TMP); X.MEMB \u003d X.MEMB? X.MEMB + 1: 1; ALERT (X.MEMB);) ) Var veku \u003d číslo (2); var bar \u003d foo (vek); // bar je teraz uzavretím, ktoré odkazuje na vek. Bar (10);

Ako sa očakávalo, každý hovoru (10) sa zvyšuje X.Memb. To, čo nemusíte očakávať, takže X sa naďalej vzťahuje na ten istý objekt ako vek! Po dvoch výzvach bude Age.Memb rovný 2! Mimochodom, nastane úniky pamäte v objektoch HTML.

Pri programovaní, zatvorení alebo v anglickom jazyku "Zatvorenie" je metóda implementácie kontextového názvu záväzného v prvej triede funkcií. Operatívne je to záznam, ktorý ukladá funkciu spolu s médiom. Životné prostredie je porovnaním každej voľnej funkcie s hodnotou alebo odkazom nazývaným uzatváraním Javascriptu. Umožňuje prístup k zachyteným premenným prostredníctvom kópií hodnôt alebo odkazov, aj keď je spôsobené mimo regiónu.

Koncepcia uzáverov

Uzávery boli vyvinuté v šesťdesiatych rokoch minulého storočia na mechanické posúdenie výrazov vo výpočte a aplikované v roku 1970 ako vlastnosť programovacieho jazyka PAL na podporu funkcií prvej triedy s lexikálnou guľou. Peter Landin definoval termín "Uzatváranie" v roku 1964 s strednou a riadiacou časťou, ktorá sa používa na SECD Stroj, aby sa posúdila výrazy Lambda, ktoré sú spojené s lexikálnym prostredím, ktoré viedli k uzavretiu alebo uzavretiu Javascriptu.

Takéto vysvetlenie bolo zahrnuté v roku 1975 ako lexikálne obmedzená verzia LISP a bola rozšírená. Lexikálne prostredie je rôzne platné premenné v programe. Skladá sa z vnútorného lekického média a odkazov na vonkajšie prostredie nazývané nelocálne premenné.

Lepkové uzávery v Javascript sú funkcie s jeho vonkajším prostredím. Rovnako ako v JavaScript, všetky premenné majú odkaz na typ. JS používa len viazanie podľa odkazov - čo zodpovedá C ++ 11 a životnosť non-lokálnych premenných zachytených funkciou je distribuovaná za životnosť funkcie.

Obvody v Javascripte sa zvyčajne objavujú v jazykoch s hodnotami prvej triedy. Takéto jazyky vám umožňujú prenášať funkcie ako argumenty. A tiež sa vrátite z funkčných hovorov a pripojte sa k názvom variabilných. To sa deje ako jednoduché typy, ako sú reťazce a celé čísla.

V tomto príklade sa objaví expresia Lambda (Lambda (\u003e \u003d (Book-Sales Book) Prahová hodnota) v rámci funkcie najpredávanejších kníh. Keď sa vypočíta expresia Lambda, obvod vytvára uzáver pozostávajúci z kódu pre expresiu Lambda a odkaz na prahovú premennú, ktorá je bezplatnou premennou v expresii Lambda. Uzatvorenie sa potom prenáša funkciou filtra, ktorá opakovane spôsobuje, že knihy by mali byť pridané do zoznamu výsledkov a ktoré by sa mali zlikvidovať.

Vzhľadom k tomu, že existuje uzáver v hodnote prahovej hodnoty, táto môže použiť ho zakaždým, keď spôsobuje jeho filtrovanie. Samotná filtrová funkcia môže byť definovaná perfektne samostatný súbor. Tu je rovnaký príklad, prepracovaný v Js. Ukážuje, ako uzávery pod kapotou v jazyku Javascript.

Kľúčové slovo tu sa používa namiesto globálnej funkcie filtra, ale zvyšok štruktúry a účinok kódu sú rovnaké. Funkcia môže vytvoriť bližšie a vrátiť ho, pretože v tomto prípade zažíva vykonávanie funkcie s premennými F a DX naďalej fungujú po derivát, aj keď vykonanie opustilo ich rozsah, a už nie sú viditeľné.

V jazykoch, bez zatvorenia, životnosť automatickej lokálnej premennej sa zhoduje s realizáciou rámu stohu, kde sa táto premenná deklaruje. V jazykoch s JavaScriptom, uzávermi a funkciami IIFE, musia premenné naďalej existovať, kým sa k nim neodkazujú žiadne existujúce zámky. To je najčastejšie implementované pomocou určitého tvaru zberu odpadu.

Výhodou uzavretia je, že si zachováva rozsah, "reťazec viditeľnosti" externého alebo "rodičovského" kontextu vykonávania. Takéto správanie môže byť použité niekoľkými spôsobmi a stal sa užitočným nástrojom na zabránenie rôznym chybám Javascript. Jedným z najčastejších je problém "slučiek".

Problém s cyklom sa vyskytuje, keď užívateľ vytvorí funkciu v cykle a očakáva, že aktuálna hodnota premennej v tomto nová funkciaAj keď sa zmení v kontexte cyklov pred volaním novej funkcie. Takto použité uzávery už nemajú referenčnú transparentnosť, a preto už nie sú čisté funkcie, ale sa bežne používajú v nečistých funkčných jazykoch, ako sú schéma. Aby ste pochopili, čo sa uzávierky v JavaScript, musíte zvážiť prípady použitia. V skutočnosti, v praxi majú mnoho aplikácií:

  1. Môžu byť použité na určenie kontrolných štruktúr. Napríklad všetky štandardné manažérske štruktúry MALLTALCK, vrátane vetiev (ak / potom / iní) a cyklov (zatiaľ čo a pre), sa určujú pomocou objektov, ktorých metódy utiahnite. Používatelia môžu tiež ľahko použiť uzávery na určenie kontrolnej štruktúry. V jazykoch, ktoré implementujú tento účel, môžete vytvoriť svoje multifunkčné médium, čo vám umožní komunikovať dôverne a zmeniť toto prostredie. Uzáver sa používa na implementáciu systémov objektov.
  2. Vytvorenie súkromných aj verejných variabilných metód pomocou šablón modulu. Vzhľadom na skutočnosť, že vrátené funkcie zdedili oblasť materskej funkcie, sú k dispozícii všetkým premenným a argumentom v tejto súvislosti.
  3. Je užitočné v situácii, keď funkcia používa rovnaký zdroj pre každú výzvu, ale tiež vytvára samotný zdroj. Táto okolnosť spôsobuje, že metóda neefektívna, ktorá je eliminovaná výlučne uzavretím.

Podľa MDN (Mozilla Developer Network) "uzávery sú funkcie s nezávislými premennými, ktoré" si pamätajú "prostredie jeho stvorenia." A spravidla, keď je funkcia dokončená, jeho lokálne premenné už neexistujú. Pochopte, ako uzáver v JavaScript pracuje, môžete zvážiť niekoľko mechanizmov. Prvá je formálna logika. Napríklad, aplikovanie funkcie LOGNAME, ktorá trvá jedno meno ako parameter a zaregistruje ho. Potom som vytvoril pre cyklus prejsť zoznam mien, nastavte 1. časový limit a potom zavolajte na funkciu logName prechádzajúcej v aktuálnom mene.

V jazyku prvej triedy môžete manipulovať rovnako ako iné typy údajov, ako napríklad int alebo reťazec. Iba tento mechanizmus umožňuje mnohým vytvoriť neuveriteľné veci, napríklad na priradenie premennej funkcie na nasledujúci hovor alebo prenášať ako parameter inej funkcie.

Tento princíp využíva mnohé štruktúry, ako aj domáce manipulátory. Po prvé, udalosť je "počúvanie", potom priraďte funkciu spätného volania, ktorá sa má zavolať zakaždým, keď je udalosť spustená.

Anonymná funkcia je funkcia bez mena. Prakticky nováčikovia sa s nimi stretávajú denne, bez pochopenia hry s číslami. Napríklad, vykonávanie pridávania, môžete prejsť premennými, napríklad:

  • var x \u003d 3;
  • y \u003d 5;
  • var z \u003d x + y.

Alebo ak nemáte v úmysle opätovne spracovať čísla: var z \u003d 3 + 5;

Ide o anonymné izby. Pre anonymné funkcie ich môžete deklarovať, keď sa používajú "na letisku" - bez prevodu premennej. Urobte si napríklad funkciu od predtým:

(Alert ("ceci est une fonchan anonyme");

Okrem toho existuje alternatívna syntax vyhlásenia o funkcii, ktorá zdôrazňuje, že obe funkcie môžu byť anonymné a odkazovať na jednoduché premenné, čo je pohodlný spôsob, ako nainštalovať funkciu spätného volania.

V skutočnosti je to rovnaký mechanizmus, ale z tohto hľadiska vám umožní vidieť, ako je funkcia zatvorená zvnútra. Ako možno vidieť, pretože funkcie sú premenné, podobne ako iné, nie je dôvod, pre ktorý nemôžu byť definované lokálne. V nulovom poradí, ako je C, C ++ a Java, všetky funkcie sú určené na jednej úrovni viditeľnosti, v tej istej triede alebo na globálnej úrovni. Na druhej strane, v JavaScript, lokálna funkcia zmizne, ako aj iné lokálne premenné, akonáhle končí materská funkcia, takže nie je viditeľná z iných funkcií.

To je vlastne ťažké, ale JavaScript má spôsob, ako sledovať viditeľnosť premenných a dokonca aj dvoma spôsobmi. Priradenie globálnej premennej v Javascript má rovnaký mechanizmus ako v jazykoch Java - komplexné objekty, polia, DOM Elements a ďalšie sú prenášané odkazom, takže v nasledujúcom kódexe:

karta var \u003d; Var Tab2 \u003d Tab.

Tam, kde, karta a tab2 sú dva odkazy na rovnakú tabuľku, technicky tieto ukazovatele ovládané zberačom odpadu. Funkcie sú tiež prenášané odkazom. Premenná globalfn už nie je skrytá. Objednávka vám umožní robiť to, čo je preukázané v príklade uzávierky Javascript.

Takto môžete extrahovať funkciu z miestneho kontextu, ak funkcia spĺňa iné lokálne premenné. Jednoduchý príklad: Automatické prírastok, funkcia, ktorá vracia celé číslo, ktoré sa zvyšuje o 1 zakaždým. Konkrétne, potreby Inc funkcie, ktoré sa správajú takto: \\ t

// retourne 0 Inc ();

// retourne 1 Inc ();

// retourne 2 Inc ();

S uzavretím to vyzerá:

funkcia makeinc () (var x \u003d 0; funkcia návratu () (návrat x ++;)) var inc \u003d makeinc ();

V poslednom riadku v okamihu, keď sa vytvorí inc premenná, nesie niektoré premenné, ktoré sú v tomto prípade x. Vytvára určitý neviditeľný objekt okolo funkcie, ktorá obsahuje túto premennú. Tento objekt je funkcia uzatvárania Javascript. Zároveň bude mať každá kópia funkcie svoje vlastné uzávierky:

var inc1 \u003d makeinc ();

var inc2 \u003d makeinc ();

Ako je možné vidieť, uzáver je veľmi užitočný v mnohých prípadoch.

Aby sa zabránilo konfliktom variabilných názvov, zvyčajne sa používajú menný priestor. V JavaScript sú menné priestory objekty ako iné.

Samozrejme, A.X a B.x nie je rovnaká premenná. Avšak, ak potrebujete spustiť skript, bez toho, aby ste si vyžadujú ukladanie premenných pre zvyšok, môže byť ako uzavretie použiť anonymnú funkciu. To dáva mierne podivnú syntax. Aj keď sú dva kódy v strede pomerne bežné, na druhej strane, funkcia, ktorá je okolo, je vykonaná "za behu". Venujte pozornosť zátvorkám () na konci. A aby ste mohli uzavrieť uzavretie, anonymná funkcia Sám musí byť obklopený okrúhlymi zátvorkami.

V tejto anonymnej funkcii použite lokálnu premennú, odsek. To je skvelý spôsob, ako zabrániť názve konfliktom alebo clusiness, ale aj proti aTAKS XSS. Vlastné premenné sú chránené, nikto ich nemôže zmeniť, aby ovplyvnili správanie skriptu.

Existuje možnosť: (funkcia () (// ...) ());

Zároveň upozorňujú na permutáciu konzol. Rozdiel medzi týmito dvoma možnosťami je veľmi ťažké vysvetliť, pretože sú spojené s tým, ako kód číta lexikálny analyzátor. V oboch prípadoch je funkcia považovaná za výraz, ale tento výraz sa neodmieta súčasne. Stačí si uvedomiť, že berie dva páry okrúhlych zátvoriek: jedna okolo funkcie a jedného za ním.

JavaScript programovanie v cykloch

Keď užívateľ vykonáva veľké objemy programovania JavaScriptu, je pre ňu ťažké vyhnúť sa cyklom. Niekto je blázon, po ktorom dospeli k myšlienke, že každá implementácia JavaScriptu má vážnu chybu. Ak má vývojár už má cyklus, že nechce konvertovať na použitie funkcie iterator, všetko, čo potrebuje urobiť, je uzavretie, v ktorom definuje nové premenné. Opravujú aktuálnu hodnotu premenných a premenujú sa na každej iterácii. Trik na zachytávanie premenných je, že externé uzavretie sa vykonáva okamžite počas aktuálnej iterácie cyklu. Môžete použiť jeden z týchto dvoch približných prístupov.

Teraz je tu ďalšie zjednodušené riešenie tohto problému, pretože kľúčové slovo je podporované ako vo Firefoxe aj v Chrome. to je kľúčové slovo Namiesto varného bloku VaR. Nechajte fungovať magicky, pretože nová premenná J je deklarovaná, hodnota ktorej hodnota je upevnená uzáverom vo vnútri cyklu. Treba však mať na pamäti, že nebude pokračovať v existovaní po skončení tej istej iterácie cyklu, pretože je lokálne.

LOOP A FUNKCIA

Pre slučku v Javascripte sa neobjaví, ako aj pre cyklus v C alebo Java. V skutočnosti to vyzerá skôr ako PHP. Najdôležitejšie vedomosti o cykloch v JS je, že nevytvárajú oblasť akcie. JS nemá blok sféry, iba funkciu objemu. Táto vlastnosť je možné zvážiť na nasledujúcom fragmente:

funkcia FOO () (var bar \u003d 1;

pre (var i \u003d 0; ja< 42; i++) {var baz = i;} /* more code */}

Je jasné, že bar je k dispozícii v celej funkcii. Pred prvým iteráciou cyklu BAZ bude nedefinovaný. Po cykle bude mať hodnotu 41 (a budem 42). Akákoľvek premenná deklarovaná kdekoľvek vo funkcii bude k dispozícii všade vo funkcii a bude platná len potom, čo bol na neho vymenovaný.

Uzávery a agregácie

Uzavretie nie je nič viac ako funkcie, vnútri iných funkcií a sú prenášané do iného kontextu. Oni sa nazývajú uzavretie, pretože blízko cez miestne premenné, ktoré sú prístupné do iných oblastí sféry. Napríklad čas, X definovaný ako parameter FOO, a var Bar \u003d Foo (2) () sa vráti 84.

Vrátená FOO FOO má prístup X. To je dôležité, pretože pomáha vývojárom vytvárať funkcie vo vnútri cyklov v závislosti od premenných cyklov. Zvážte tento fragment, ktorý priradí kliknutie procesora na rôzne prvky:

// Prvky sú pole 3 DOM Elements var Hodnoty \u003d ["FOO", "BAR", "BAZ"];

i.< l; i++) {var data = values[i];

prvky [i] .onclick \u003d funkcia () (upozornenie (údaje);

Hodnota, ktorú budú používať upozornenie pri stlačení, budú rovnaké pre každého, a to baz. V tom čase sa psovod udalostí nazýva už dokončený. JS nemá blokovú oblasť, t.j. Všetky manipulátory používajú odkaz na rovnakú dátovú premennú. Po slučke bude táto hodnota hodnoty. Každá premenná vyhlásenie vytvára jedno miesto v pamäti pamäte. Na tento údaj sa opäť zmenia a opäť zmení poloha v pamäti nezmenená.

Každý handler udalostí má prístup do rovnakej pozície v pamäti. Jediným riešením je vstúpiť do inej oblasti, ktorá "opravuje" aktuálnu hodnotu údajov. JS má len rad funkcií. Preto sa zadá ďalšia funkcia. Príklad:

funkcia CreateeVentoHandler (X) (Funkcia návratu () (upozornenie (x);

pre (var i \u003d 0, l \u003d prvky.

i.< l; i++) {var data = values[i];

prvky [i] .onclick \u003d CreateeVendHandler (dáta);

Funguje, pretože hodnota dát bude uložená v miestnej oblasti, CreaeeVendler a táto funkcia sa vykonáva na každej iterácii. Môže byť napísaný v krátkom čase, pomocou spustiteľných funkcií okamžite:

pre (var i \u003d 0, l \u003d prvky.

i.< l; i++) {var data = values[i];

prvky [i] .onclick \u003d (funkcia (x) (funkcia () (upozornenie (x);

Praktický príklad uzavretia v Javascript

Ak užívateľ uzávierky v prehliadači, môže naraziť na problém, pretože môže vykonať akúkoľvek chybu syntaxe. Ak sa kód vykonáva priamo v prehliadači, šance sú veľmi vysoké, aby nedošlo k zostaveniu procesu zostavovania webového puzdra. Možné riešenia:

funkčná práca (názov) (

funkcia návratu (téma) (

console.log (čo je $ (téma) v $ (meno));

práce ("javascript") ("uzáver");

Po prvé, funkcia sa nazýva a argument názvov sa prenáša. Teraz táto funkcia slovnej zásoby tiež vráti funkciu, ktorá tiež prijíma argument tém. Táto funkcia registruje výstup a výstup má prístup k premennej.

Oblasť funkcií zasvätených osôb nie je obmedzená na túto funkciu, takže koncepcia sa nazýva uzavretie, pretože má prístup k tejto oblasti externý parameter. Vrátená funkcia má prístup k externej lexikálnej oblasti alebo kontexte. Keď vývojár volá funkciu, ktorá ho tiež vráti, potom prvý funkcia premennej Vždy k dispozícii pre internú funkciu. Ďalší príklad s nasledujúcim kódom.

Príklad vnútornej funkcie

Prečítajte si viac o uzavretí v Javascript môže byť povedané v druhom príklade. Teraz je toto naplnenie prostredia zničené, ale názov parametra stále existuje. Vytvorí sa nové vnútorné funkčné médium, čo je anonymná funkcia. Má prístup do oblasti vonkajšieho lexikálneho prostredia.

Tak, v premennej vonkajšieho prostredia, stále existuje takým spôsobom, že anonymná funkcia má prístup k menom premennej výtlačkov v konzole, napríklad "Čo je uzáver v Javascript". Vnútorná anonymná funkcia //main.js

funkcia továreň () (VAR Products \u003d;

i ++) (Products.Push (funkcia () (Console.LOG (I);

) Spiatočné produkty;

) Var myap \u003d továreň ();

Výsledok tohto príkladu je skôr nevýznamný a rovný 2.

Keď mydlo je mydlo () nazývané vonkajšie kontextové premenné, vždy 2, pretože v cykle je stav falošný v I<2, поэтому при этом значение i равно 2, а во время вызова нужно напечатать значение в консоль так, она всегда пишет 2. То же самое для мыла - soap ().

Vytváranie funkcií "za behu"

Môžete vytvoriť funkcie továreň - funkčné, ktoré vykonávajú vlastné úlohy. Výslednou funkciou z továrne funkcií bude uzáver, skladovacie médium vytvárania.

var FuncfaFactory \u003d Funkcia (NUM1) (RETURN NUM1 * NUM2;

Vyššie uvedené vám umožňuje preniesť jedno funkčné číslo. Potom FunctionFactory vráti uzáver, zapamätajte si hodnotu NUM1. Výsledná funkcia znásobuje pôvodnú num1-násobok hodnoty num2, ktorý sa prenáša, keď sa volá.

var mult5 \u003d funkcia (5);

var mult10 \u003d funkčný (10);

Vyššie uvedené jednoducho vytvorí funkcie MULT5 a MULTO10. Teraz môžete odkazovať na ktorúkoľvek z týchto funkcií prejdite na nové číslo, ktoré sa má vynásobiť o 5 alebo 10. Teraz môžete vidieť výsledok.

Uzavretie je jedným z najvýkonnejších funkcií JavaScriptu, ale nemožno ho používať správne bez pochopenia podstaty. Sú to relatívne ľahko vytvoriť náhodou, to je, ako nebezpečné uzávery Javascript. Ich tvorba má potenciálne škodlivé účinky, najmä v niektorých relatívne bežných prostrediach webového prehliadača. Aby sa zabránilo náhodnému kolízii s nevýhodami a využite výhody, ktoré ponúkajú, je potrebné pochopiť ich mechanizmus.

V tomto článku sa budem snažiť vysvetliť oblasti viditeľnosti a uzavretia v Javascriptov, v ktorom mnohé problémy s skúsenosťami.

Úvod

Existuje pomerne niekoľko článkov v sieti, ktoré sa snažia vysvetliť oblasti viditeľnosti a uzavretia, ale vo všeobecnosti by som povedal, že väčšina z nich nie je úplne zrozumiteľná. Okrem toho, v niektorých článkoch sa predpokladá, že ste naprogramovaní pred 15 ďalšími jazykmi, aj keď si myslím, že väčšina ľudí, ktorí písali na JavaScript, má len skúsenosti s HTML a CSS, a nie v C alebo Java.

Účelom tohto článku je preto vysvetliť všetkým - aký je rozsah viditeľnosti a uzavretia, ako pracujú, a čo je čo najdôležitejšie, aká je ich výhoda. Pred čítaním tohto článku musíte poznať základné pojmy o premenných a funkciách v Javascript.

Oblasť viditeľnosti

Rozsah znamená, kde sú k dispozícii premenné a funkcie a v tomto kontexte sú vykonané. Voľná \u200b\u200balebo funkcia môže byť definovaná v globálnej alebo miestnej oblasti viditeľnosti. Premenné majú takzvanú funkciu viditeľnosti funkcie a funkcie majú rovnaký rozsah ako premenné.

Globálny rozsah

Keď je niečo globálne, znamená to, že je k dispozícii odkiaľkoľvek vo vašom kódexe. Príklad:

var opice \u003d "gorilla"; Funkcia Greetvisitor () (Upozornenie na vrátenie ("Ahoj Vážení Blog Reader!");)

Ak bol tento kód vykonaný vo webovom prehliadači, potom oblasť viditeľnosti by bola okno, thever bude k dispozícii pre všetko, čo je vykonané v okne.

Miestny rozsah

Na rozdiel od oblasti globálnej viditeľnosti je lokálna oblasť viditeľnosti, keď je niečo definované a dostupné len v určitej časti kódu, ako je funkcia. Príklad:

funkcia TalkDirty () (var hovorí \u003d "OH, Ty Little VB Lovník, vy"; Návrat Alert (hovorí);) Upozornenie (hovorí); // vyvolali chybu

V tomto príklade je premenná príslovia k dispozícii iba vo vnútri funkcie Talksidty, mimo ktoré nie je definovaná. POZNÁMKA: Ak ste boli identifikovaní tým, že hovoríte, že bez kľúčového slovar, automaticky sa stane globálnym.

Okrem toho, ak máte vnorené funkcie, interná funkcia bude mať prístup k funkciám, v ktorých je vložená, ako aj premenné:

funkcia CappitazeName (návrat FirstName.Tuppercase ();) var kapitalizovaný \u003d kapitalizánoMename (); návrat kapitalizovaný;) upozornenie (SaveName ("Robert")); // vráti "Robert"

Ako ste práve videli, vnútorná funkcia kapitalizény nemusí prenášať žiadne parametre, pretože Má plný prístup k parametrom prvotného pomeru vo vonkajšej funkcii sauname. Pre väčšiu jasnosť zvážte ďalší príklad:

funkčné súrodencov () (var súrodenci \u003d ["JOHN", "LIZA", "Peter"]; funkcia SiDingCount () (var SiblingsDengry \u003d Siblings.Length; Späť SiblingsDengry;) Funkcia JoinsibliedNames () (Návrat "Mám" + SiblingCount () + "Súrodenci: nn" + súrodenci.join ("n");) návratu joinsibliednames ();) upozornenie (súrodenci ()); // výstupy "Mám 3 súrodencov: John Liza Peter"

Ako vidíte, obe interné funkcie majú prístup k súrodencov a každá interná funkcia má prístup k inej vnútornej funkcii rovnakej úrovne (v tomto prípade, JoinsiBliedNames má prístup k SiblingCount). Siblingslength premenná vnútri súdnutého kosťom je k dispozícii iba vo vnútri tejto funkcie, t.j. V tejto oblasti viditeľnosti.

Okruh

Teraz, keď máte jasnú predstavu o oblastiach viditeľnosti, pridáme k nim uzavretie. Okruhy sú výrazy, zvyčajne funkcie, ktoré môžu pracovať so súborom premenných v určitom kontexte. Alebo jednoduchšie slová, vnútorné funkcie, ktoré sa týkajú lokálnych premenných externých funkcií uzáverov. Napríklad:

funkcia Pridať (X) (Return X + Y;);) var pridanie5 \u003d pridať (5); var no8 \u003d add5 (3); ALERT (NO8); // vráti 8.

Blimey! Čo sa tu deje? Poďme zistiť:

1. Keď zavoláme funkciu Pridať, vráti funkciu.

2. Táto funkcia uzatvára kontext a zapamätá sa, ako je parameter X v tomto čase (t.j. v tomto prípade hodnota 5)

3. Ak je výsledok funkcie Pridať priradený k premennej add5, vždy bude vedieť, čo X bolo pri vytváraní tejto premennej.

4. Variabilný add5 sa vzťahuje na funkciu, ktorá vždy pridá hodnotu 5 na akýkoľvek argument, ktorý ho prenáša.

5. To znamená, že keď nazývame doplnok5 s hodnotou 3, zloží číslo 5 a 3 a vráti sa 8.

V skutočnosti, v Javascriptovom svete, funkcia add5 vyzerá takto:

funkcia add5 (y) (návrat 5 + y;)

Notorický problém cyklov
Koľkokrát ste vytvorili cykly, v ktorých ste chceli priradiť hodnotu I Akým spôsobom, napríklad prvok a pochopil, že sa vrátila len posledná hodnota, ktorú som sa vrátila?

Vhodnýpríťažlivosť

Pozrime sa na tento nesprávny kód, ktorý vytvára 5 prvkov , pridáva hodnotu I ako text na každý prvok a onclick, ktorý sa očakáva, že bude vyrábať upozornenie s hodnotou I pre tento odkaz, t.j. Rovnakú hodnotu ako v texte prvku. Potom sa prvky pridajú do orgánu dokumentov:

<5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; link.onclick = function () { alert(i); }; document.body.appendChild(link); } } window.onload = addLinks;

Každý prvok obsahuje správny text, t.j. "Link 0", "Link 1", atď. Ale bez ohľadu na odkaz, ktorý ste klikli, ukazuje upozornenie s číslom 5. Čo je to záležitosť? Dôvodom je, že hodnota premennej sa zvyšuje o 1 s každou iteráciou cyklu a pretože Udalosť OnClick nie je vykonaná, ale jednoducho sa vzťahuje na prvok Zvýšenie hodnoty.

Preto, cyklus pokračuje v práci, kým sa nedostane rovný 5, čo je posledná hodnota pred odchodom z funkcie AddLinks. Ďalej, pri každom spustení udalosti Onclick, posledná hodnota I sa užíva.

Náležitý odvolanie

Čo musíte urobiť, je vytvoriť uzavretie. V dôsledku toho, keď aplikujete hodnotu I na udalosť udalosti Onclick , Budem prideliť hodnotu I v tom čase. Napríklad, takto:

funkcia AddLinks () (pre (VAR I \u003d 0, Link; I<5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; link.onclick = function (num) { return function () { alert(num); }; }(i); document.body.appendChild(link); } } window.onload = addLinks;

Pomocou tohto kódu, ak kliknete na prvý prvok, upozornenie vydá "0", na druhom mieste - "1" atď. Riešením je, že vnútorná funkcia aplikovaná na udalosť Onclick vytvára uzavretie, v ktorom sa Num parameter odvolaní, t.j. k hodnote I v tom čase.

Táto funkcia "si pamätá" požadovanú hodnotu a potom môže vrátiť príslušnú číslicu, keď sa spustí udalosť Onclick.

Zabezpečené funkcie

Funkcie bezpečnosti sú takými funkciami, ktoré sa začínajú vykonávať okamžite a vytvárajú ich uzavretie. Príklad:

(Funkcia () (var psom \u003d "nemecký ovčiak"; upozornenie (psa);)) (); Upozornenie (psa); // vráti nedefinované.

Takže premenná psa je k dispozícii len v tomto kontexte. Myslite, skrytý premenný pes ... Ale moji priatelia, najzaujímavejšia vec začína týmto! Vyriešil náš problém s cyklom, a to je tiež základ pre model modulu Yahoo Javascript.

Yahoo JavaScript Modul vzor

Podstatou tohto vzoru je, že používa bezpečnostnú spustiteľnú funkciu na vytvorenie uzavretia, preto umožňuje používať súkromné \u200b\u200ba verejné vlastnosti a metódy. Jednoduchý príklad:

var Osoba \u003d Funkcia () (// Private var Mena \u003d "Robert"; návrat (GetName: Funkcia () (Názov návratu), SETNAME: Funkcia (NewName) (Názov \u003d NewName;)););); Upozornenie (osoba.Name); // nedefinované upozornenie (osoba.getname ()); // "robert" osoba.setname ("Robert Nyman"); ALERT (osoba.getname ()); // "Robert Nyman"

Výhodou tohto prístupu je, že sa môžete definovať, ktorý bude otvorený vo vašom objekte (a môže byť zmenený), a ktorý sa uzavrie, ktorý nikto nemôže kontaktovať alebo zmeniť. Názov Premenná je ukrytá mimo kontextu funkcie, ale je prístupná k funkciám GetName a SetName, pretože Vytvárajú uzávery, v ktorých je odkaz na premennú názvu.

Záver

Úprimne dúfam, že po prečítaní tohto článku, nováčikovia a skúsení programátori dostali jasnú predstavu o tom, ako v oblasti viditeľnosti a uzáveru JavaScriptu. Otázky a recenzie sú vítané, a ak máte niečo dôležité, aby som niečo nechal, potom môžem aktualizovať článok.

Dobré kódovanie!

Okruhy v Javascript. Používa na skrytie variabilných hodnôt a funkcií ukladania. Ide o to, že keď je zatvorené, je vytvorená jedna funkcia, v ktorej sú určené premenné a ktoré v dôsledku jeho prevádzky vráti svoju vnorenú funkciu. Potom sa v ňom (v hlavnej funkcii) vytvorí vstavaná funkcia, v ktorej sa niektoré operácie uskutočňujú s premennými funkčnými premennými a ktoré vracajú výsledok týchto operácií. Ďalej je hlavná funkcia rovná premennej - táto premenná môže byť vyzvaná, koľko času a však hodnoty premenných hlavnej funkcie budú uložené a aktualizované. Je "zatvorená".

Ako viete, v Javascript oblasti viditeľnosti lokálnych premenných (vyhlásené za slovo VaR) Je to telo funkcie, v ktorej sú definované.

Ak deklarujete funkciu vo vnútri inej funkcie, prvý dostane prístup k premenným a argumentom týchto:

Kód: Funkcia OUTERFN (MYARG) (
Var myvar;
Funkcia InnerFN () (
// má prístup k Myvaru a Myargu
}
}

Takéto premenné sú súčasne existovať a zostávajú dostupné vnútorné funkcie aj po externej funkcii, v ktorej sú definované.

Zvážte príklad - funkciu, ktorá vráti počet eigenvalues:

Kód: Funkcia CreateCounter () (
var numberfcalls \u003d 0;
Funkcia návratu () (
Return ++ NumberOfcalls;
}
}
var fn \u003d CreateCounter ();
fn (); // jeden
fn (); // 2.
fn (); // 3.

V tomto príklade funkcia vrátená podľa CreateCounter používa NumberOfcalls premennú, ktorá uloží požadovanú hodnotu medzi jeho hovormi (namiesto okamžite zastavenie jeho existencie s návratom CreateCounter).

Je to pre tieto vlastnosti, že takéto "vnorené" funkcie v Javascript sa nazývajú uzávery (termín, ktorý prišiel z programovacích funkčných jazykov) - "bližšie" premenné a argumenty funkcie, v ktorej sú definované.

Uplatňovanie uzáverov

Zjednodušuje malý príklad vyššie - Odstráňte potrebu samostatne zavolať funkciu CreateCounter, čo ho pripojí a volá ho okamžite po jeho reklame:

Kód: var fn \u003d (funkcia () () (
var numberfcalls \u003d 0;
Funkcia návratu () (
Return ++ NumberOfcalls;
}
})();

Takýto dizajn nám umožnil viazať údaje do funkcie, ktorá zostala medzi jeho výzvami jedným z použití uzáverov. Inými slovami, s pomocou nich môžeme vytvárať funkcie, ktoré majú variabilný stav.

Ďalšie dobré využívanie uzáverov - vytvorenie funkcií, zase, tiež vytváranie funkcií - čo by sa nazývali T.N. Metaprogramovanie.
Napríklad:

Kód: Var CreateHellofunction \u003d Funkcia (názov) (
Funkcia návratu () (
Upozornenie ("Hello" + názov);
}
}
varovehelhabrahabr \u003d createhellofunction ("habrahabr");
sayhelhabrahabr (); // Upozornenia "Ahoj, Habrahabr"

Vďaka uzavretiu, vrátená funkcia "si pamätá" parametre prevedené na funkcie vytvárania, ktoré potrebujeme pre tento druh vecí.

Podobná situácia nastane, keď nevrátime internú funkciu a visíme na ľubovoľnú udalosť - pretože podujatie vzniká po splnení funkcie, uzáver opäť pomáha stratiť údaje prenášané, keď je vytvorený procesor dát.

Zvážte o niečo zložitejší príklad - metóda, ktorá spája funkciu na konkrétny kontext (t.j. objekt, na ktorý toto slovo v ňom uvedie).

Kód: Funkcia.Protototyp.bind \u003d Funkcia (kontext) (
var fn \u003d to;
Funkcia návratu () (
Návrat FN.APPLE (kontext, argumenty);
};
}
var helopage \u003d (
Názov: "Habrahabr",
Init: Funkcia () ()
ALERT ("Ahoj," + toto .Name);
}
}
//Window.onload \u003d helopage.Init; // Alertnul by nedefinoval, pretože To označuje okno
window.onload \u003d helopage.init.bind (helopage); // teraz všetko funguje

V tomto príklade, s pomocou uzáveru, funkcie, spustenej väzby "ohm, si spomína pôvodnú funkciu a kontext, ktorý mu bol priradený.

Ďalej zásadne odlišné používanie uzáverov - ochrana údajov (zapuzdrenie). Zvážte nasledujúci návrh:

Kód: (funkcia () ()

})();

Samozrejme, vo vnútri uzavretia máme prístup ku všetkým externým údajom, ale má svoje vlastné. Kvôli tomu môžeme oblúžiť časť kódu ako dizajn s cieľom zatvoriť lokálne premenné z prístupu vonku. (Jedným z príkladov jeho použitia je možné vidieť v zdrojovom kóde o knižnici JQuery, ktorý obklopuje uzavretie všetkého svojho kódu tak, aby nie je na to, aby premenné iba na ňu).

Tam je však jeden spojený s takýmto použitím pasce - význam slova, ktorý sa stratí vo vnútri uzavretia. Vyrieši sa takto:

Kód: (funkcia () ()
// najvyššie bude pokračovať
)). Volanie (toto);

Zvážte ďalší príjem z rovnakej série. To bolo široko popularizované svojimi vývojármi Rámec Yahoo UI, volal mu "vzor modulu" a písanie celého článku na ňom v oficiálnom blogu.
Dovoľte nám, aby sme mali objekt (Singleton), ktorý obsahuje akékoľvek metódy a vlastnosti:

Kód: var mymodule \u003d (
Názov: "Habrahabr",
SayPreved: Funkcia (meno) (
Upozornenie ("prevzaté" + name.Touppercase ())
},
this.saypreved (this.name);
}
}
Mymodule.sayprevedtohabrahabr ();

S pomocou uzavretia môžeme vytvoriť metódy a vlastnosti, ktoré sa nepoužívajú mimo objektu, súkromné (t.j. dostupné len mu):

Kód: var mymodule \u003d (funkcia () () ()
Var name \u003d "habrahabr";
Funkcia SayPreved () () (
Upozornenie ("Zavedel" + name.Touppercase ());
}
Návrat (
SayPrevedtohabrahabr: Funkcia () () () (
Povedzte (meno);
}
}
})();
Mymodule.sayprevedtohabrahabr (); // Upozornenia "Zavzali HABRAHABR"

Nakoniec chcem opísať spoločnú chybu, ktorá mnohí jazdí na hlúposti v prípade nevedomosti o tom, ako fungujú uzávery.

Dovoľte nám, aby sme mali rad odkazov a našou úlohou je, aby to tak, že keď kliknete na každú aplikáciu Allort, zobrazí sa jeho poradové číslo.
Prvé rozhodnutie, ktoré príde na myseľ, vyzerá takto:

Kód: pre (var i \u003d 0; ja< links.length; i++) {
ALERT (I);
}
}

V skutočnosti sa ukázalo, že keď kliknete na ľubovoľný odkaz, zobrazí sa rovnaké číslo - hodnota odkazov. Prečo sa to deje? V súvislosti s uzavretím, oznámená pomocná premenná pretrváva, s a v súčasnosti, keď klikneme na odkaz. Od tej doby, keď cyklus už prešiel, zostal som rovní počtu odkazov - táto hodnota, ktorú sme vidíme pri kliknutí.

Tento problém je vyriešený takto:

Kód: pre (var i \u003d 0; ja< links.length; i++) {
(Funkcia (i) (
Odkazy [i] .onclick \u003d funkcia () () (
ALERT (I);
}
)) (i);
}

Tu, s pomocou iného uzáveru, my "tieň" variabilný I, vytvorte ho kópiu v miestnej oblasti viditeľnosti v každom kroku cyklu. Vďaka tomu všetko funguje teraz ako koncipované.

To je všetko. Tento článok, samozrejme, netvrdí, že je vyčerpávajúci, ale niekto, dúfam, že stále pomáha prísť na to.

zy.
Pre ukladanie medzi hovormi uľahčuje používanie FUNC_NAME.attr Typ:

Kód: Funkcia (reset) (reset) (
ak (reset ||! grtit.cnt) grilit.cnt \u003d 0;
návrat groupit.cnt ++;