JS bezárása. JavaScript áramkörök: gyakorlati példa, jellemzők és szabályok

Üdv mindenkinek! Ebben a cikkben megnézzük mi a lezárás a JavaScript-ben.

Ez egy nagyon egyszerű téma, de megértést igényel. Először nézzük meg, mi történik a funkció belsejében.

Funkció üdvözlés (név) (
// lexicalenvironment \u003d (Név: "Nikolai", Szöveg: Nincs meghatározva)
var text \u003d "hello" + név;
// lexicalenvironment \u003d (Név: "Nikolai", Szöveg: "Hello, Nikolai")
Riasztás (szöveg);
}

Üdvözlés ("Nikolai");

Mi történik itt és mi van Lexicalenvironment? Tedd ki.

Amikor a funkciót hívják, létrehoz egy objektumot LexicalenvironmentAmelyben minden helyi változót és funkciót rögzítenek, valamint utalnak a külső hatókörre (később később). A mi esetünkben helyi változó van név.amely azonnal értéke (aztán át) és ez a "Nikolai". Az egyik cikkben már írtam azonban, emlékeztetem Önt, hogy az Intermator mindent előre ismeri az összes változót. Ez az, hogy a funkció kezdetén már van változó sZÖVEG., a tolmács tud róla, de mivel még nem érte el a változó egyfajta változó hozzárendelését, egyenlő határozatlan.. Most változó értéket és objektumunkat rendelünk hozzá Lexicalenvironment Változtatások. Az ingatlan sZÖVEG. Megegyezik azzal a ténnyel, hogy rögzítettük ("Hello, Nikolai" a mi esetünkben). A funkció kidolgozása után az objektum Lexicalenvironment Elpusztítani. A későbbi hívásokkal újra létrehozva stb.

Most a következő példához fordulunk. Mondja meg, mi lesz ez az esetben?

Var b \u003d 2;
X (a) függvény (
riasztás (A + B);
}
X (1);

Gondolat? Azt hiszem, a legtöbb válaszol, hogy a 3. szám jelenik meg, és ez a helyes válasz, de elmondhatod, hogy a tolmács megtudta a változóról b.? Végtére is, nem a test funkcióban van. Ha nem, értjük.

Valójában javaScript. Van egy rejtett ingatlan neve [] . Amikor a funkciót bejelentették, mindig valahol bejelentett. Ez a funkció lehet egy másik funkcióban, lehet egy globális objektum stb. A mi esetünkben a funkciót a globális objektumban kell bejelenteni. ablak.ezért vagyon x. [] \u003d ablak.

Var b \u003d 2;
X (A) funkció (// x. [] \u003d Ablak
// lexicalenvironment \u003d (A: 1) -\u003e ablak
riasztás (A + B);
}
X (1);

Ez a nyílnak van egy objektuma Lexicalenvironment - Ez a külső hatókörre való hivatkozás, és ez a kapcsolat a vagyon állapítható meg. [] . Így az objektumban Lexicalenvironment Hivatkozunk egy külső objektumra. ablak.. Amikor a tolmács változó keres, akkor először keresi az objektumban LexicalenvironmentAztán, ha nem találta változóat, akkor a linkre néz, egy külső hatókörbe kerül, és ott keres, és a végéig kereste. Ha bárhol nem találta ezt a változót, hiba lesz. A mi esetünkben változó a. A tolmács az objektumból fog venni Lexicalenvironmentés a változó b. Az objektumból ablak.. Természetesen, ha helyi változónk van b. Valamilyen értékkel az objektumban rögzül Lexicalenvironment És ezt követően innen, és nem a láthatóság külső területén.

FONTOS! Ne feledje, hogy a tulajdonság [] Azt telepíti a hely, ahol a funkciót bejelentették, és nem okozott, ezért az alábbi kód megjeleníti a 3. számot, és nem 5, amint néhányan gondolkodhatnak.

BAR B \u003d 2;
X (a) függvény (
riasztás (A + B);
}

Y () függvény () (
var b \u003d 4;
X (1);
}

Mindez csak egyértelmű volt, hogy megértsük, hogyan működik, és könnyebb volt, hogy megértsük, hogyan működik a bezárások. És most közvetlenül a cikk témájához fordulunk.

Ahogy mondtam, az objektum Lexicalenvironment Minden alkalommal, amikor megsemmisül minden alkalommal, miután a végrehajtó funkció és újra létrejön, amikor újra hív. Azonban, ha meg akarjuk menteni ezeket az adatokat? Azok. Azt akarjuk, hogy mindent rögzítünk Lexicalenvironment Most már megmaradt, és használták a következő kihívásokra? Ez azért van bezárás.

Funkció üdvözlés (név) (
// lexicalenvironment \u003d (Név: "Nikolai")
Visszatérési funkció () (// [] \u003d lexicalenvironment
Riasztás (név);
};
}

Var func \u003d üdvözlés ("Nikolai");
üdvözlés \u003d null;
FUNC ();

Lássuk, mit tettünk. Először létrehozunk egy funkciót Üdvözlet.amelyben a név elhalad. A funkció létrehoz egy objektumot Lexicalenvironmentahol az ingatlan létrehozása (helyi változónk) név. És a "Nikolai" nevet hozzárendeli. És most fontos: Visszatérünk egy másik funkciót a funkciótól, amelyen belül származunk Éber. változó név.. Továbbá változtatunk egy változót fUNC. A függvényből visszaküldött érték Üdvözlet.És ez az érték a mi funkciónk, amely egy nevet jelenít meg. most mi Üdvözlet. Hozzárendeljük nULLA. Csak megsemmisítjük a funkciónkat Üdvözlet.Azonban, amikor hívjuk fUNC.Ezután látni fogjuk a változó értékét név.("Nikolai") funkciók Üdvözlet.. Hogyan lehet mondani? És nagyon egyszerű. A dolog az, hogy a visszaküldött funkciónk is van egy ingatlan. [] amely a láthatóság külső terjedelmére utal, és ez a külső hatókörünk egy objektum Lexicalenvironment Funkciónk Üdvözlet.. Ezért annak ellenére, hogy töröljük a funkciónkat Üdvözlet., egy tárgy Lexicalenvironment Nem törölve és a memóriában maradt, és a memóriában marad, amíg legalább egy link. Van ez a link - a visszaküldött funkció, amely a változót használja név. e létesítményből Lexicalenvironment.

Tehát adjuk meg a definícióját mi a zárás.

Áramkör - Funkció minden olyan változóval, amely elérhető.

Nos, a cikk nagyon terjedelmes, de ez csak azért, mert megpróbáltam leírni a bezárás teljes munkáját. A konszolidáláshoz egyszerű példát akarok hozni - egy métert a vizsgált témával. Kérjük, vegye figyelembe a kódot, és írjon a megjegyzésekben, hogyan és miért működik. Ha nem értesz valamit, kérdezhetsz egy kérdést is. Kösz a figyelmet!

Funkció makecounter () () (
var currenticount \u003d 0;

Visszatérési funkció () () () () (
CurrentCount ++;
Visszatérési árampont;
};
}

Var számláló \u003d makecounter ();
Számláló ();
Számláló ();
Riasztás (számláló ()); // 3.

BAN BEN JavaScript funkciók Nemcsak a másik után, hanem a másik belsejében is leírható. Ha van egy funkciója egy másik, a belső poundáció hozzáférést biztosít a váltakozó külső funkcióhoz.

Külső (x) függvény (var tmp \u003d 3, belső (Y) (Y) (Y) (riasztás (x + y + (++ tmp)); // eltávolítja a 16) belső (10);) külső (2);

Ez a kód mindig 16, mert a belső funkció az X-t látja, ami a funkukció külső változója. BAN BEN ez az eset Argument funkció. Szintén belső () láthatja a TMP-t a külső ().

Ezt úgynevezett bezárás vagy bezárás. Ha pontosabban a lezárást külső funkciónak nevezik, és mindent benne van bezárási környezetnek vagy záró környezetnek.

Néha azt mondják, hogy a lezárás egy olyan funkció, amely visszaadja a függvény, akkor helytelenül, annak érdekében, hogy nevét a funkció a lezárás, hogy a belső funkció csak a változó kívülről hatálya alól.

Funkció FOO (X) (VAR TMP \u003d 3, Visszatérési funkció (Y) (riasztás (X + Y + (++ TMP)); // szintén figyelmezteti a 16) var bar \u003d foo (2); // Bar most lezárás. (10);

A fenti funkció 16 megjelenítést fog megjeleníteni 16, mivel a Foo befejezése utáni sáv továbbra is hozzáférést biztosít az X-hez és a TMP-hez, még akkor is, ha a sávváltozó maga nem tartozik olyan hatályba, amelyben bejelentették őket.

Ugyanakkor, mivel a TMP változó még mindig a bár bezárása alatt van, továbbra is növeli az egyes próbabáblákat.

Itt a legegyszerűbb példa Áramkör:

Var a \u003d 10; Function Test () (console.log (A); // Következtetés 10 console.log (B); // Következtetés 6) VAR B \u003d 6; teszt ();

Ha a JavaScript funkciót elindítja, a környezetet hozza létre, vagyis az összes változó lista, nemcsak az érvek és változók, hanem kívülről is kijelentették, hanem kívül is "a "És" B ".

Egynél több lezárást hozhat létre egy környezetben, visszaküldheti őket egy tömbbe, objektumra vagy globális változóhoz való csatolásra. Ebben az esetben mindannyian dolgoznak ugyanaz X vagy TMP érték az egyes példányok létrehozása nélkül.

Mivel az X példánkban a szám, akkor az értéke másolat Az FOO-ban, mint az x.

Másrészt a JavaScript mindig hivatkozásokat használ, amikor az objektumok továbbításra kerülnek. Ha úgynevezett Foo-t egy objektummal, mint argumentum, akkor a visszatérő bezárás visszaadná a linket az eredeti objektumhoz!

Funkció foo (x) (var tmp \u003d 3, visszatérési funkció (y) (riasztás (x + y + tmp); x.memb \u003d x.memb? X.memb + 1: 1; riasztás (x.memb);) ) Vari \u003d szám (2); var bar \u003d foo (életkor); // Bar most az életkorra utaló zárás. (10);

A várt módon minden hívás (10) növeli az X.Memb-t. Amit nem számíthat arra, hogy az X továbbra is ugyanazon objektumra utal az életkorra! Két hívás sáv után age.memb egyenlő 2! By the way, a memória szivárgás HTML objektumok előfordulnak.

A programozás, lezárnak, vagy az angol nyelvű változat „Zárás” egy módszer végrehajtására kontextus kötő neve az első osztályú funkciókat. Operatív módon, ez egy bejegyzés, amely tárolja a funkciót a közeggel együtt. A környezet az egyes ingyenes funkciók összehasonlítása, amelynek értéke vagy hivatkozása a JavaScript bezárásával. Lehetővé teszi a rögzített változókhoz való hozzáférést az értékek vagy linkek másolatai révén, még akkor is, ha a régión kívül esik.

A záróelemek fogalma

Zárások hoztak létre az 1960-as években a mechanikus értékelést kifejezések kiszámítása és alkalmazott 1970-ben, mint egy eleme a PAL programozási nyelv támogatása az első osztályú funkciók lexikális szférában. Peter Landin meg pontosan a „lezárás” 1964-ben a közép- és vezérlő rész használják a SECD gép, hogy értékelje a lambda kifejezéseket kapcsolódó lexikális környezetet, ami a záró, vagy bezárással JavaScript.

Az ilyen magyarázatot 1975-ben Lexikusan Limited Lisp verziójaként vették fel, és széles körben elterjedt. A lexikai környezet számos érvényes változó a programban. Ez egy belső lecikai közegből áll, és utaljon egy külső környezetre, amelyet nem zárt változóknak neveznek.

A JavaScript nyelvi lezárása a külső környezet jellemzői. Mint a JavaScript esetben az összes változónak van egy linkje. A JS csak a C ++ 11-nek felel meg, és a függvény által rögzített nem helyi változók élettartama a funkció élettartama alatt van elosztva.

A JavaScript áramkörei általában az első osztályú értékekkel rendelkező nyelveken jelennek meg. Ezek a nyelvek lehetővé teszik a funkciókat, mint argumentumok. És térjen vissza a funkcióhívásokból, és csatoljon változó nevekhez. Ez olyan, mint az egyszerű típusok, például a húrok és az egész számok.

Ebben a példában a lambda kifejezés (Lambda (\u003e \u003d (Book-Sales Könyv) Threshold) belül megjelenő bestseller-KÖNYVEK funkciót. Amikor a lambda expressziós számítjuk, az áramkör létrehoz egy záróelem, amely a kódot az expresszióját a lambda és a referencia a Threshold változó, amely nem egy szabad változó belül lambda kifejezés. A lezárás ezután továbbítják Filter funkció, ami miatt ismételten, hogy melyik könyvet kell egészíteni a találati listát, és amelyet meg kell semmisíteni.

Mivel a küszöbérték értéke van, az utóbbi minden alkalommal használhatja a szűrő okait. A szűrő funkció maga tökéletesen definiálható külön fájl. Itt ugyanaz a példa, újraírva a JS-ben. Megmutatja, hogy a JavaScript-i motorháztető lezárása hogyan működik.

A kulcsszó a globális szűrőfunkció helyett, de a szerkezet többi része és a kód hatása ugyanaz. A funkció lehet létrehozni egy szorosabb és vissza, mert ebben az esetben tapasztalható a függvény végrehajtása változók F és DX továbbra is működni után DERIVATÍV, még ha a végrehajtás bal körük, és azok már nem láthatók.

A nyelvek lezárása nélkül az automatikus helyi változó élettartama egybeesik a verem keretének végrehajtásával, ahol ezt a változót bejelentik. A JavaScript-szel rendelkező nyelveken az iif, változók lezárása és funkcióinak továbbra is fenn kell maradniuk, amíg a meglévő záraknak hivatkoznak rájuk. Ezt leginkább a szemétgyűjtés valamilyen formájával valósítja meg.

A lezárás előnye, hogy megőrzi a külső vagy "szülői" végrehajtás kontextusának hatókörét, "láthatósági láncát". Az ilyen viselkedés többféle módon is használható, és hasznos eszközré vált, hogy megakadályozza a különböző JavaScript hibákat. Az egyik leggyakoribb a "hurkok" problémája.

A ciklussal kapcsolatos probléma akkor fordul elő, ha a felhasználó létrehoz egy funkciót a ciklusban, és elvárja a változó aktuális értékét ebben Új funkcióMég akkor is, ha új funkciót hív le a ciklusok keretében. Az így használt lezárások már nem rendelkeznek referencia átláthatósággal, és ezért már nem tiszta funkciók, azonban általában tisztátalan funkcionális nyelveken használják őket, például a rendszert. Annak érdekében, hogy megértsük, mi zárul a JavaScript, meg kell fontolnia az esetek használatát. Valójában a gyakorlatban sok alkalmazásuk van:

  1. Használhatók a kontrollstruktúrák meghatározására. Például az összes szabványos Smalltalk irányítási struktúrákat, beleértve ágak (if / then / else), kerékpár (while és a for) segítségével határozzuk meg tárgyakat, amelyek módszerek figyelembe lezárások. A felhasználók könnyen használhatják a zárószerkezet meghatározását is. A célt végrehajtó nyelveken létrehozhat többfunkciós médiumot, amely lehetővé teszi, hogy bizalmasan kommunikáljon és módosítsa ezt a környezetet. A lezárás az objektumrendszerek megvalósítására szolgál.
  2. A magán- és nyilvános változó módszerek létrehozása modul sablonok segítségével. Annak a ténynek köszönhetően, hogy a visszaadott funkciók örökölték a szülői funkció területét, ezek ebben az összefüggésben minden változó és érv rendelkeznek.
  3. Hasznos olyan helyzetben, ahol a funkció ugyanazt az erőforrást használja az egyes hívásokhoz, de létrehoz egy erőforrást is. Ez a körülmény miatt a módszer hatástalanítja, ami kizárólag zárással megszűnik.

Az MDN (Mozilla Developer Network) szerint a lezárások független változókkal vannak ellátva, amelyek "emlékeznek" a teremtés környezetére. " És általában, amikor a funkció befejeződött, a helyi változók már nem léteznek. Értsd meg, hogyan működik a JavaScript bezárása, több mechanizmust is figyelembe vehet. Az első egy hivatalos logika. Például a logname funkció alkalmazásával, amely egy nevet tartalmaz, mint paraméter és regisztrálja. Ezután létrehozom a ciklust, hogy átmegyek a nevek listáján, állítsa be az 1. időtúllépést, majd hívja az aktuális nevében átadott logname funkciót.

Az első osztályú nyelven ugyanúgy manipulálhat, mint más típusú adatok, például int vagy string. Csak ez a mechanizmus lehetővé teszi számos, hihetetlen dolgokat, például, hogy rendelni egy változó funkció későbbi hívást, vagy továbbítja azt paraméterként más funkciót.

Ezt az elvet számos struktúra, valamint a DOM eseménykezelők használják. Először is, az esemény "hallgatás", majd hozzárendel egy visszahívási funkciót, amelyet az esemény bekapcsolnak.

Az Anonymous funkció egy név nélküli függvény. Gyakorlatilag az újonc programozók naponta találkoznak velük, anélkül, hogy a játékot megértenék. Például az adagolási művelet végrehajtása, akkor változhatnak, például:

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

Vagy ha nem kíván újra feldolgozni a számokat: var z \u003d 3 + 5;

Ezek névtelen szobák. Az anonim funkciókért kijelentheti őket, amikor a "Fly" -on "- anélkül, hogy átadná a változót. Például, vegye figyelembe a funkciót a korábban:

(Riasztás ("ceci est une fonchan anonyme");

Ezenkívül alternatív szintaxis az olyan függvénynyilatkozatnak, amely hangsúlyozza, hogy mindkét funkció névtelen lehet, és az egyszerű változókra utalhat, ami a visszahívási funkció telepítésének kényelmes módja.

Tény, hogy ugyanaz a mechanizmus, de ebből a szempontból lehetővé teszi, hogy lássa, hogyan zárul le a funkció belülről. Mint látható, mivel a funkciók változók, mint mások, nincs ok arra, hogy melyeket nem lehet helyben definiálni. A nulla sorrendben, például a C, C ++ és a Java esetében az összes funkciót a láthatóság egyik szintjén, ugyanabban az osztályban vagy globális szinten határozzák meg. Másrészt, a JavaScript, a helyi funkció eltűnik, valamint más lokális változók, amint a szülő funkció véget ér, így nem látható egyéb funkciók.

Ez valójában nehéz, de a JavaScript rendelkezik a változók láthatóságának nyomon követésére, sőt két módon. Hozzárendelése globális változó a JavaScript ugyanaz a mechanizmus, mint a Java - összetett objektumok, tömbök, DOM elemeket és mások által továbbított referencia, így a következő kódot:

vAR TAB \u003d; VAR Tab2 \u003d fül.

Hol, a Tab és a Tab2 két link ugyanazon az asztalra, technikailag ezek a szemétgyűjtő által vezérelt mutatók. A funkciókat referenciaként is továbbítják. A GLOBALFN változó már nem rejtett. A megrendelés lehetővé teszi, hogy megtegye a JavaScript bezárási feladat példáján.

Ez az, hogy kivonhatja a funkciót a helyi kontextusból, ha a funkció más helyi változókat kielégít. Egy egyszerű példa: Automatikus növekmény, olyan függvény, amely visszaad egy egész számot, amely 1 minden egyes alkalommal növekszik. Pontosabban, az INC funkciók igényei, amelyek a következőképpen viselkednek:

// Retourne 0 Inc ();

// retourne 1 inc ();

// retourne 2 inc ();

A lezárással úgy néz ki:

funkció Mekény () (VAR X \u003d 0, Visszatérési funkció () (visszatérés x ++;)) Var Inc \u003d MeReinc ();

Az utolsó sorban abban a pillanatban, amikor az Inc változót létrehozza, olyan változókat hordoz, amelyek körül vannak, ebben az esetben x. Egy bizonyos láthatatlan objektumot hoz létre a változó funkciót tartalmazó funkció körül. Ez az objektum egy JavaScript bezárási funkció. Ugyanakkor a funkció minden másolatának saját lezárása lesz:

var inc1 \u003d Mekény ();

var inc2 \u003d Mekény ();

Amint látható, a lezárás sok esetben nagyon hasznos.

A változó név konfliktusainak elkerülése érdekében a névteret általában használják. A JavaScript esetben a névterek olyan objektumok, mint bármely más.

Természetesen az A.X és a B.X nem azonos változó. Ha azonban csak el kell indítania a parancsfájlt, anélkül, hogy a többiek mentése lenne, anonim függvény használható bezárásként. Ez kissé furcsa szintaxist ad. Bár a középső két kódok meglehetősen gyakoriak, másrészt a körülötte lévő funkció "a repülésen" történik. Figyeljen a zárójelekre () a végén. És hogy bezárni, névtelen funkció Maga kerek zárójelben kell körülvéve.

Ebben az névtelen funkcióban helyi változó, bekezdés. Ez egy nagyszerű módja annak, hogy megakadályozzák a név konfliktusokat vagy ügyetlenséget, hanem ellen ataks XSS. Az egyéni változók védettek, senki sem tudja megváltoztatni őket, hogy befolyásolják a szkript viselkedését.

Van egy lehetőség: (funkció () (// ...) ());

Ugyanakkor felhívja a figyelmet a zárójelek permutációjára. A két lehetőség közötti különbség meglehetősen nehéz megmagyarázni, mert a kódot a lexikai analizátor jelöli. Mindkét esetben a funkció kifejezésnek tekinthető, de ezt a kifejezést nem becsülik meg egyidejűleg. Csak emlékezzen arra, hogy két pár kerek zárójelét veszi: az egyik a funkció körül és egy mögötte.

JavaScript programozás ciklusokban

Amikor a felhasználó nagy mennyiségű JavaScript programozást végez, nehéz a ciklusok elkerülése érdekében. Valaki őrült, amely után jönnek az ötlet, hogy a JavaScript bármilyen végrehajtása komoly hibát követ el. Ha a fejlesztőnek már van egy ciklusa, hogy nem akarja átalakítani az iterátor funkcióját, mindent, amit meg kell tennie, olyan lezárás, amelyben új változóit határoz meg. Megjavítja a változók aktuális értékét és változó minden iteráción. A változók rögzítésére szolgáló trükk az, hogy a külső zárás azonnal a ciklus jelenlegi iterációja alatt történik. Használhatja a két hozzávetőleges megközelítés egyikét.

Most van egy másik egyszerűsített megoldás erre a problémára, mivel a kulcsszó támogatja mind a Firefoxban, mind a Chrome-ban. Ez kulcsszó Var változó blokk helyett. Hagyja mágikusan dolgozni, mert egy J új változó van bejelentve, amelynek értéke, amelynek értéke a ciklus bezárásával van rögzítve. Mindazonáltal szem előtt kell tartani, hogy a ciklus ugyanazon iterációjának vége után nem létezik, mivel helyben van.

Hurok és funkció

A JavaScript hurokhoz nem jelenik meg, valamint C vagy Java ciklus. Valójában úgy néz ki, mint a PHP. A JS-ben lévő ciklusok legfontosabb ismerete, hogy nem hoznak létre cselekvési területet. A JS-nek nincs szféra blokkja, csak a hangerő funkciója. Ez a tulajdonság figyelembe vehető az alábbi fragmensen:

funkció Foo () (var bar \u003d 1;

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

Nyilvánvaló, hogy a sáv az egész funkcióban érhető el. A baz ciklus első iterációja előtt meghatározatlan lesz. A ciklus után 41 értéket fog kapni (és 42 leszek). Így a függvényben bárhol bármely változó mindenhol elérhető lesz a funkcióban, és csak akkor érvényes, ha azt kinevezték neki.

Redőnyök és aggregáció

A bezárás nem más, mint a funkciók, más funkciókon belül, és továbbítják más kontextusba. Úgy hívják őket bezárásnak, mivel közel vannak a helyi változókon, azaz a szféra más területeihez. Például a FOO paraméter, és a var bar \u003d foo (2) (2) () vissza fog térni 84.

A visszaadott Foo funkció hozzáféréssel rendelkezik X. Ez minden fontos, mert segít a fejlesztőknek a ciklusváltozóktól függően a belső ciklusok létrehozásához. Tekintsük ezt a fragmenst, amely hozzárendeli a processzort különböző elemekre:

// Elements egy sor 3 DOM elem VAR érték \u003d ["foo", "bar", "baz"];

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

elemek [i] .onclick \u003d funkció () (riasztás (adatok);

Az érték, amelyet figyelmeztetést fognak használni, mindenki számára ugyanaz lesz, nevezetesen BAZ. Ekkor az eseménykezelőt úgy hívják, hogy már befejeződött. A JS-nek nincs blokkterülete, vagyis Minden rakodás a linket ugyanazon adatváltozóhoz használja. A hurok után ez az érték érték lesz. Minden változó nyilatkozat létrehoz egy helyet a tárolási memóriában. Az alábbiakban ezeket az adatokat újra és újra megváltoztatják, a memória pozíciója változatlan marad.

Minden eseménykezelő hozzáférést biztosít a memóriában. Az egyetlen megoldás egy másik területre való belépés, amely "javítja" az aktuális adatértéket. A JS csak számos funkcióval rendelkezik. Ezért egy másik funkciót írunk be. Példa:

funkció Createeventhandler (X) (Visszatérési funkció () (riasztás (x);

a (var i \u003d 0, l \u003d elem.lengnth esetében;

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

elemek [i] .onclick \u003d createeventhandler (adatok);

Működik, mert az adatértéket a helyi területen tárolja, a Createeventhandler és ezt a funkciót minden iteráción végezzük. Röviden írható, a végrehajtható funkciók azonnali használatával:

a (var i \u003d 0, l \u003d elem.lengnth esetében;

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

elemek [i] .onclick \u003d (funkció (x) (funkció () (riasztás (x);

A JavaScript bezárás gyakorlati példája

Ha a felhasználó a böngészőben lévő kód fölé zárul, akkor probléma merülhet fel, mivel bármilyen szintaktikai hibát okozhat. Ha a kódot közvetlenül a böngészőben hajtja végre, akkor az esélyek nagyon magasak, hogy ne fordítsák össze a webpack összeállításának folyamatát. Lehetséges megoldások:

funkció munka (név) (

visszatérési funkció (téma) (

console.log (mi a $ (topic) a $ (név));

munka ("JavaScript") ("bezárás");

Először is, a funkciót hívják, és a név argumentumát továbbítják. Most ez a szókincs funkciója is visszaad egy olyan funkciót, amely elfogadja a témák argumentumát is. Ez a funkció regisztrálja a kimenetet, és a kimenet hozzáférést biztosít a változóhoz.

A bennfentes funkciók területe nem korlátozódik erre a funkcióra, ezért a koncepciót bezárásnak nevezik, mivel hozzáférhet ez a területhez külső paraméter. A visszaküldött funkció hozzáférést biztosít a külső lexikai területhez vagy kontextushoz. Amikor a fejlesztő olyan funkciót hív, amely szintén visszaadja, majd először hívják változó funkció Mindig elérhető a belső funkcióhoz. További példa a következő kóddal.

Egy belső funkció példája

Tudjon meg többet a JavaScript bezárásáról a második példában. Most ez a teljesítési környezet megsemmisül, de a paraméter neve még mindig létezik. Új belső funkcionális közeg jön létre, amely névtelen funkció. Hozzáférés a külső lexikai környezet területéhez.

Így egy változót a külső környezet még mindig létezik olyan módon, hogy egy névtelen függvényt hozzáfér a neve változó kiírja a konzolra, például: „mi az a lezárás JavaScript”. Belső névtelen funkció //main.js

funkciógyár () (Var Products \u003d;

i ++) (Products.Push (funkció () (konzol.log (i);

) Visszatérő termékek;

) Var szappan \u003d gyár ();

Ennek a példának eredménye meglehetősen jelentéktelen és egyenlő 2.

Ha a szappan szappan (), úgynevezett külső kontextusváltozó, mindig 2, mert a ciklusban az állapot hamis<2, поэтому при этом значение i равно 2, а во время вызова нужно напечатать значение в консоль так, она всегда пишет 2. То же самое для мыла - soap ().

Funkciók létrehozása "a repülésen"

Létrehozhat egy funkciógyárat - működőképes funkciót, amely egyéni feladatokat hajt végre. A funkciók gyárából származó függvény lezárás, tárolási médium lesz.

vAR funkciófactory \u003d funkció (NUM1) (NUM1 * NUM2;

A fenti lehetővé teszi, hogy átadja az egyik funkciószámot. Ezután a funkciófunkciós visszaadja a lezárást, emlékezve a NUM1 értékre. A kapott funkció megszorozza az eredeti Num2-es számát, amelyet akkor továbbítanak.

vAR MULT5 \u003d funkciófúvó (5);

vAR Mult10 \u003d funkciófúvó (10);

A fenti egyszerűen létrehozza a MULT5 és MULT10 funkciót. Most ezen funkciók bármelyikére hivatkozhat, ha új számot ad meg 5 vagy 10-ig. Most már láthatja az eredményt.

A lezárás az egyik legerősebb JavaScript funkció, de nem használható helyesen a lényeg megértése nélkül. Viszonylag könnyű létrehozni a véletlenszerűen, ezért veszélyes lezárja a JavaScriptet. A teremtésük potenciálisan káros hatással jár, különösen néhány viszonylag közös böngésző környezetben. Annak elkerülése érdekében, hogy elkerüljék a véletlen ütközést hátrányokkal, és kihasználják az általuk kínált előnyöket, meg kell érteni mechanizmusukat.

Ebben a cikkben megpróbálom megmagyarázni a JavaScript láthatóságát és bezárását, amelyben sok tapasztalat nehézséget okoz.

Bevezetés

A hálózatban meglehetősen néhány cikk van, amelyek megpróbálják megmagyarázni a láthatóság és a bezárás területeit, de általában azt mondanám, hogy a legtöbbjük nem teljesen érthető. Ezenkívül néhány cikkben feltételezzük, hogy 15 másik nyelv előtt van programozva, bár azt gondolom, hogy a JavaScript-nél író emberek többsége csak a HTML és a CSS-ben tapasztalható, és nem C vagy Java.

Következésképpen az a célja, hogy ez a cikk elmagyarázza az összes - milyen terjedelmű a láthatóság és a lezárás, ahogy dolgozik, és ami a legfontosabb, amit az előnye. A cikk elolvasása előtt meg kell ismernie a JavaScript változókkal és funkciókkal kapcsolatos alapfogalmakat.

Láthatósági terület

A hatóköre azt jelenti, hogy a változók és funkciók rendelkezésre állnak, és amelyekben végrehajtják őket. A változó vagy funkció a láthatóság globális vagy helyi területén lehet meghatározni. A változóknak az úgynevezett funkciói láthatóságának funkciója van, és a funkciók ugyanolyan hatályúak, mint változók.

Globális hatókör

Amikor valami globális, azt jelenti, hogy bárhol elérhető a kódodban. Tekintsünk egy példát:

var monkey \u003d "gorilla"; Funkció Greetvisor () (Visszatérő riasztás (Hello Kedves Blog Reader! ");)

Ha ezt a kódot végeztünk egy böngészőben, akkor a láthatóság terület lenne ablakban theover elérhető lesz mindent, ami végre a Window.

Helyi hatály

A globális láthatósági területével ellentétben a helyi láthatósági terület akkor van, ha valamit meg kell határozni, és csak a kód valamelyik részében, például egy függvényben kapható. Tekintsünk egy példát:

funkció Talkdirty () (var mondd \u003d "Ó, te kis vb szerető, te"; return riasztás (mondás);) riasztás (mondás); // hibát dobott

Ebben a példában a mondásváltozó csak a TalkDirty funkció belsejében érhető el, amelyen kívül nincs meghatározva. Megjegyzés: Ha Var kulcsszó nélkül azonosítaná, akkor automatikusan globális lesz.

Ezenkívül, ha beágyazott függvényekkel rendelkezik, a belső funkció hozzáférhet a beágyazott funkciókhoz, valamint a változókhoz:

function Capitalizename (Return FirstName.Tuppercase ();););););); // visszatér a "Robert"

Ahogy csak láttál, a belső függvény kapitalizenévnek nem kell semmilyen paramétert továbbítani, mert Teljes mértékben hozzáférhet az első név paraméterhez az SAVENAME külső funkciójához. A nagyobb tisztaság érdekében fontolja meg egy másik példát:

funkció testvérek () (VAR testvérek \u003d ["John", "Liza", "Peter"]; Funkció siblingcount () (var siblingslingsh \u003d siblings.lengnth, return siblingslength;) Funkció JoinsiblingNames () () + "Testvérek: nn" + testvérek.join ("n");) vissza a JoinsiblingNames (); // kimenetek "3 testvére van: John Liza Peter"

Amint láthatja, mind a belső funkciók hozzáférhetnek a testvérek tömbjéhez, és minden belső funkciónak hozzáférhet egy másik belső funkcióhoz (ebben az esetben a JoinsiblingNames hozzáférést biztosít a Sibledcounthoz). Azonban a SIBLINGSLENGTH változó belső SIBLINGCOUNT csak belülről ezt a funkciót, azaz a Ezen a területen látható.

Áramkör

Most, hogy világosabb ötleted van a láthatósági területekről, hozzáadunk bezárást számukra. Az áramkörök kifejezések, általában olyan funkciók, amelyek bizonyos kontextusban egy változókészülékkel dolgozhatnak. Vagy egyszerűbb szavak, belső funkciók, amelyek a külső funkciók helyi változókra vonatkoznak. Például:

funkció hozzáadása (x) (RETURN X + Y;);) var add5 \u003d add (5); var n8 \u003d add5 (3); Riasztás (NO8); // visszatér 8.

Blimey! Mi folyik itt? Tedd ki:

1. Amikor felhívjuk az Add funkciót, visszaadja a funkciót.

2. Ez a funkció bezárja a kontextust, és megjegyzi, hogy az X paraméter ebben az időben van (azaz ebben az esetben, az 5. érték)

3. Ha az eredmény az Új funkciót rendelt a ADD5 változtatható, mindig tudni fogja, hogy mi volt az X létrehozásakor ezt a változót.

4. A változó ADD5 olyan függvényre vonatkozik, amely mindig hozzáadja az 5-ös értéket minden olyan érvhez.

5. Ez azt jelenti, hogy ha hívjuk ADD5 a 3 értéket, akkor hajtsa száma 5 és 3, és vissza fog térni 8.

Tény, hogy a JavaScript-világban az Add5 funkció így néz ki:

funkció hozzáadása5 (Y) (Visszatérés 5 + Y;)

A ciklusok hírhedt problémája
Hányszor létrehozott olyan ciklusokat, amelyekben bármilyen módon akarta hozzárendelni I-t, például egy elemet, és megértette, hogy csak az utolsó értéket visszaküldtem?

Rosszfellebbezés

Nézzük meg ezt a hibás kódot, amely 5 elemet hoz létre Egészíti értéke i, mint a szöveget, hogy az egyes elemek és onclick, amely várhatóan termel figyelmeztető jelzés értéke I erre utalás, azaz Ugyanaz az érték, mint az elem szövegében. Ezután az elemeket hozzáadják a dokumentum testéhez:

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

Minden elem tartalmazza a helyes szöveget, azaz "LINK 0", "LINK 1", stb. De bármi is van a rákattintott link, ez jelzi az 5. számú figyelmeztetést. Mi az a kérdés? Ennek az az oka, hogy a változó értéke 1 a ciklus minden iterációjával nő, és mivel Onclick esemény nem hajtható végre, de egyszerűen az elemre vonatkozik Az érték növekszik.

Ezért a ciklus továbbra is működik, amíg meg nem egyenlő 5, ami az utolsó érték, mielőtt kiment az addlinks funkcióból. Továbbá, minden alkalommal, amikor az ONCLICK esemény bekövetkezik, az utolsó i érték.

Megfelelő fellebbezés

Amit meg kell tennie, hozzon létre egy bezárást. Ennek eredményeként, ha az I. értéket az OnClick esemény eseményre alkalmazza , Akkor az I. számú értéket hozzárendeljem. Például, mint ez:

funkció addlinks () (a (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;

Ezzel a kóddal, ha rákattint az első elemre, a figyelmeztetés "0" -ot, a második - "1" stb. A megoldás az, hogy a belső funkciót alkalmazva a onclick eseményt hoz létre, lezárás, amelyben a NUM paraméter fellebbezések, azaz az I. számú értékre.

Ez a funkció "emlékszik" a kívánt értékre, majd visszaküldheti a megfelelő számjegyet, ha az ONCLICK esemény bekapcsolja.

Biztonságos végrehajtó funkciók

A biztonságos végrehajtó funkciók olyan funkciók, amelyek azonnal elkezdenek végrehajtani és a bezárás létrehozását. Tekintsünk egy példát:

(Funkció () (var kutya \u003d "német juhász"; riasztás (kutya);)) (); Riasztás (kutya); // A meg nem definiált.

Tehát a kutyaváltozó csak ebben a kontextusban érhető el. Gondolj, rejtett változó kutya ... de a barátaim, a legérdekesebb dolog kezdődik ezzel! Megoldotta a problémát egy ciklussal, és ez is alapja a Yahoo JavaScript modulmintának.

Yahoo JavaScript modul minta

Ennek a mintának a lényege, hogy biztonságos végrehajtható funkciót használ a lezárás létrehozásához, ezért lehetővé teszi a magán- és nyilvános tulajdonságok és módszerek használatát. Egyszerű példa:

var személy \u003d funkció () (// private var name \u003d "Robert"; Return (GetName: Funkció () (Visszatérési név), SetName: Funkció (NewName) (Név \u003d NewName)););) (); riasztás (person.name); // undefined riasztás (személy.getname ()); // "robert" person.setnév ("Robert Nyman"); riasztás (person.getname ()); // "Robert Nyman"

Ennek a megközelítésnek az az előnye, hogy meghatározhatja magát, amely nyitva lesz az objektumában (és módosítható), és lezárt, amely senki sem tud kapcsolatba lépni vagy változtatni. A névváltozó el van rejtve a funkció kontextusán kívül, de elérhető a GetName és SetName funkciókhoz, mert Olyan zárókat hoznak létre, amelyekben a név változóhoz kapcsolódnak.

Következtetés

Őszintén remélem, hogy miután elolvasta ezt a cikket, az újonnan érkezettek és a tapasztalt programozók világosabb elképzelést kaptak arról, hogy a JavaScript munkaterületek a láthatóság és a bezárások. A kérdések és vélemények szívesen láthatók, és ha valami fontos, hogy elengedj valamit, akkor frissíthetem a cikket.

Jó kódolás!

A JavaScript áramkörök. A változó értékek és tárolási funkciók elrejtésére szolgál. A lényeg az, hogy lezárták, egy funkciót hoznak létre, amelyben a változókat megadják, és amely a működésének eredményeképpen visszaadja beágyazott funkcióját. Aztán, hogy (a fő funkció), beágyazott funkció létre, amelyben egyes műveletek készülnek változó funkció változókat, és amelyek eredményét adja vissza ezeket a műveleteket. Ezután a fő funkció egyenlő egy változóval - ez a változó felhívható, hogy mennyi időre és azonban a fő funkció változóinak értékei tárolódnak és frissülnek. Ő "zárva van".

Mint tudod, a JavaScriptben a helyi változók láthatóságának területe (Nyilatkozat Var Word) Ez a funkció teste, amelyen belül meghatározzák őket.

Ha egy másik funkció belsejében működő funkciót hirdet, az első hozzáférést kap az utóbbi változókhoz és érvekhez:

Kód: funkció külsőfn (myarg) (
Var myvar;
Innerfn funkció () () () () () (
// hozzáférést biztosít Myvar és Myarghoz
}
}

Ugyanakkor az ilyen változók továbbra is fennállnak, és továbbra is rendelkezésre állnak a belső funkciókat még a definiált külső függvény után is.

Tekintsünk egy példát - olyan funkciót, amely visszaadja az eInenvalues \u200b\u200bszámát:

Kód: Funkció: Createcounter () (
varuckofcalls \u003d 0;
Visszatérési funkció () () () () (
Visszatérés ++ numberofcalls;
}
}
var fn \u003d createcounter ();
fn (); //egy
fn (); // 2.
fn (); // 3.

Ebben a példában a Createcounter által visszaadott függvény a NUMBEROFCALLS változót használja, amely megmenti a kívánt értéket a hívások között (ahelyett, hogy azonnal megállítja a létezését a Createcounter visszatérésével).

Ezeknek a tulajdonságoknak van szó, hogy a JavaScript-ben lévő ilyen "beágyazott" funkciókat lezárásoknak nevezik (a funkcionális nyelvek programozásából származó kifejezés) - "közelebb" a függvények változásai és argumentumai.

A zárások alkalmazása

Egyszerűsíti egy kis példát fent - távolítsa el a szükségességet, hogy külön-külön hívja fel a Createcounter funkciót, így a mellékletet, és hívja fel azonnal a hirdetés után:

Kód: var fn \u003d (funkció () () () () (
varuckofcalls \u003d 0;
Visszatérési funkció () () () () (
Visszatérés ++ numberofcalls;
}
})();

Az ilyen kialakítás lehetővé tette számunkra, hogy kötődnek az adatokat a funkciót, ami maradt fenn a kihívásokat egyik felhasználási bezárások. Más szóval, segítve olyan funkciókat hozhatunk létre, amelyek változó állapotúak.

A bezárások egyéb jó felhasználása - a funkciók létrehozása, viszont, szintén funkciókat is létrehoznak - mit neveznek a t.n. Metaprogramozás.
Például:

Kód: var createhellofunkció \u003d funkció (név) (
Visszatérési funkció () () () () (
Riasztás ("hello," + név);
}
}
var Sayhellohabrahabr \u003d CreateHellofunkció ("habrahabr");
sayhellohabrahabr (); // figyelmeztetések "hello, habrahabr"

A bezárásnak köszönhetően a visszaadott függvény "emlékszik" a paraméterek átkerülnek a létrehozás funkcióira, hogy szükségünk van erre a fajta dolgokra.

Hasonló helyzet akkor fordul elő, ha nem adunk vissza egy belső funkciót, és bármilyen eseményen lógunk - mivel az esemény felmerül a funkció teljesítése után, a lezárás ismét segít abban, hogy ne veszítse el a továbbított adatokat, ha az adatgyűjtő létrehozásakor továbbítják az adatokat.

Tekintsünk egy kicsit összetettebb példát - olyan módszert, amely egy adott kontextushoz kapcsolódik (azaz a tárgy, amelyhez ez a szó jelzi benne).

Kód: funkció.prototype.bind \u003d funkció (kontextus) (
var fn \u003d ez;
Visszatérési funkció () () () () (
Vissza az fn.Apply (kontextus, argumentumok);
};
}
var hellopage \u003d (
Név: "habrahabr",
init: funkció () () () () () (
Riasztás ("hello," + this.name);
}
}
//Window.onload \u003d hellopage.init; // Alertnul határozatlanul, mert Ez jelzi az ablakot
window.Onload \u003d hellopage.init.bind (hellopage); // most minden működik

Ebben a példában, a segítségével egy lezárás, egy függvény, kiváltott Bind „Ohm, emlékszik a kezdeti funkció és a kontextus hozzárendelve.

Ezután alapvetően különböző lezárások használata - adatvédelem (Egységbezárás). Tekintsük a következő tervezést:

Kód: (funkció () () (

})();

Nyilvánvaló, hogy a záróelem belsejében hozzáférhetünk minden külső adathoz, de sajátja van. Ennek köszönhetően a kód egy részét úgy tervezhetjük, mint egy kialakítás, hogy lezárja a helyi változókat a hozzáférés kívülről. (A használat egyik példája látható a JQuery könyvtár forráskódjában, amely körülveszi az összes kód bezárását, hogy ne adja ki a változókat csak rá.

Van azonban, amely az ilyen felhasználási csapdához kapcsolódik - a szó értelmének jelentése a zárás belsejében elveszett. A következőképpen oldódik meg:

Kód: (funkció () () (
// A legmagasabb ez folytatódik
)). Hívás (ez);

Tekintsünk egy másik vételt ugyanabból a sorozatból. Széles körben népszerűsítette a fejlesztők a Yahoo Ui keret szólította „Module Pattern”, és írt egy teljes cikket rajta a hivatalos blog.
Legyen egy objektum (singleton), amely bármilyen módszert és tulajdonságot tartalmaz:

Kód: var mymodule \u003d (
Név: "habrahabr",
SayPreved: Funkció (név) (
Riasztás ("megelőző" + név.touppercase ())
},
ez.SayPreved (this.name);
}
}
Mymodule.sayprevedtohabrahabr ();

A zárás segítségével olyan módszereket és tulajdonságokat hozhatunk létre, amelyeket az objektumon kívül nem használnak (azaz csak neki kapható):

Kód: var mymodule \u003d (funkció () () () (
Var name \u003d "habrahabr";
Funkció SayPreved () () () (
Riasztás ("megelőző" + név.touppercase ());
}
Visszatérés (
SAYPREVEVEDTOHABRAHABR: Funkció () () () () () () ()
SayPreved (név);
}
}
})();
Mymodule.sayprevedtohabrahabr (); // figyelmeztetések "Előfordított Habrahabr"

Végül azt szeretném leírni egy gyakori hibát, amelyet sokan rettentek, ha nem tudják, hogy milyen lezárások a munka.

Legyen egy sor linkek, és feladata annak, hogy így kattintson az egyes allállokra, a sorszám megjelenik.
Az első döntés, amely az elmere vonatkozik:

Kód: a (var i \u003d 0; i)< links.length; i++) {
Riasztás (i);
}
}

Valójában kiderül, hogy ha bármilyen linkre kattint, ugyanazt a számot jeleníti meg - a Links.lengnth értéke. Miért történik ez? A bezárással kapcsolatban a bejelentett segédváltozó továbbra is fennáll, és abban a pillanatban, amikor rákattintunk a linkre. Mivel abban az időben a ciklus már elmúlt, én továbbra is egyenlő a kapcsolatok száma - ezt az értéket mi látható, ha rákattint.

Ez a probléma a következőképpen oldódik meg:

Kód: a (var i \u003d 0; i)< links.length; i++) {
(Funkció (i) (
Linkek [i] .onclick \u003d funkció () () () () ()
Riasztás (i);
}
)) (i);
}

Itt a segítségével egy másik bezárása, mi „árnyék” változó azt, ami egy-egy példányát a helyi terület láthatóságát mindegyik ciklusban lépést. Ennek köszönhetően minden, amit most fogalmaznak meg.

Ez minden. Ez a cikk természetesen nem állít, hogy kimerítő, de valaki, remélem, még mindig segít megmutatni.

zy.
A hívások közötti mentés megkönnyíti a FUNC_NAME.ATTR típus használatát:

Kód: Funkciószám (visszaállítás) (
ha (reset ||! countit.cnt) countit.cnt \u003d 0;
visszatérési countit.cnt ++;