JS Închidere. Circuite în JavaScript: exemplu practic, caracteristici și reguli

Salutare tuturor! În acest articol vom privi ce este o închidere în JavaScript.

Acesta este un subiect destul de simplu, dar necesită înțelegere. În primul rând, să ne uităm la ceea ce se întâmplă în interiorul funcției.

Funcție salut (nume) (
// lexicalenvironment \u003d (Nume: "Nikolai", text: nedefinit)
var text \u003d "salut," + nume;
// lexicalenvironment \u003d (Nume: "Nikolai", text: "salut, nikolai")
Alertă (text);
}

Salut ("nikolai");

Ce se întâmplă aici și ce este Lexicalnvironment.? Să ne dăm seama.

Când funcția este numită, ea creează un obiect Lexicalnvironment.În care sunt înregistrate toate variabilele și funcțiile locale, precum și referirea la domeniul extern (despre acest lucru mai târziu). În cazul nostru, avem o variabilă locală nume.care are imediat o valoare (apoi trecem) și acest "Nikolai". Într-unul din articolele pe care le-am scris deja, îți amintesc că interpretorul știe totul despre toate variabilele în avans. Este vorba de acest lucru că la începutul funcției are deja o variabilă tEXT., interpretul știe despre asta, dar din moment ce nu am ajuns încă la atribuirea acestei variabile de un fel, este egal cu nedefinit.. Acum atribuim o valoare variabilă și obiectul nostru Lexicalnvironment. Schimbări. Proprietatea sa tEXT. Ea devine egală cu faptul că am înregistrat ("Bună ziua, Nikolai" în cazul nostru). După ce funcția a fost elaborată, obiectul Lexicalnvironment. Distruge. Cu apelurile ulterioare, acesta va fi creat din nou, etc.

Acum ne întoarcem la următorul exemplu. Spune-mi ce va fi derivat în acest caz?

Var b \u003d 2;
Funcția x (a) (
alertă (A + B);
}
x (1);

Gând? Cred că cel mai mult a răspuns că numărul 3 va fi afișat și acesta este răspunsul potrivit, totuși vă puteți spune cum a învățat interpretul despre variabila b.? La urma urmei, nu este în funcția corpului. Dacă nu, să înțelegem.

De fapt in javaScript. Există o proprietate ascunsă numită [] . Când funcția este declarată, este întotdeauna declarată undeva. Această caracteristică poate fi într-o altă funcție, poate fi într-un obiect global etc. În cazul nostru, funcția este declarată în obiectul global. fereastră., prin urmare, proprietate x. [] \u003d fereastră.

Var b \u003d 2;
Funcția x (a) (// x. [] \u003d Fereastră
// lexicalenvironment \u003d (A: 1) -\u003e Fereastră
alertă (A + B);
}
x (1);

Această săgeată are un obiect Lexicalnvironment. - Aceasta este o referire la domeniul de aplicare extern, iar această legătură este stabilită de proprietate. [] . Deci, în obiect Lexicalnvironment. Vom avea o referire la un obiect extern. fereastră.. Când interpretul caută o variabilă, atunci el îl caută mai întâi în obiect Lexicalnvironment., atunci, dacă nu a găsit o variabilă, atunci se uită în legătură, merge într-un domeniu extern și o căută acolo acolo și astfel până la capăt. Dacă nu a găsit această variabilă nicăieri, va fi o eroare. În cazul nostru, variabil a. Interpretul va lua de la obiect Lexicalnvironment., și variabila b. De la obiect fereastră.. Desigur, dacă avem o variabilă locală b. Cu o anumită valoare, acesta va fi înregistrat în obiect Lexicalnvironment. Și ulterior vor fi luate de acolo și nu din domeniul extern de vizibilitate.

IMPORTANT! Amintiți-vă că proprietatea [] Acesta este instalat de locul în care funcția a fost declarată și nu este cauzată, de aceea codul de mai jos afișează numărul 3 și nu 5, așa cum s-ar putea gândi unii.

Bar B \u003d 2;
Funcția x (a) (
alertă (A + B);
}

Funcția y () (
var b \u003d 4;
x (1);
}

A fost un preludiu doar pentru a înțelege cum funcționează totul și a fost mai ușor să înțelegeți cum funcționează închiderile. Și acum ne întoarcem direct la subiectul articolului.

Așa cum am spus, obiectul Lexicalnvironment. De fiecare dată când este distrusă de fiecare dată după executarea funcției și este creată din nou la re-apelarea. Cu toate acestea, dacă vrem să salvăm aceste date? Acestea. Vrem ca totul să fie înregistrat Lexicalnvironment. Acum, a fost păstrat și a fost folosit pentru următoarele provocări? Este pentru asta închidere.

Funcție salut (nume) (
// lexicalenvironment \u003d (Nume: "Nikolai")
Funcția de returnare () (// [] \u003d lexicalenvironment
Alertă (nume);
};
}

Var func \u003d salut ("nikolai");
salut \u003d null;
Func ();

Să vedem ce am făcut. Mai întâi creăm o funcție salut.în care este trecut numele. Funcția creează un obiect Lexicalnvironment.unde este creată proprietatea (variabila noastră locală) nume. Și i se atribuie numele "Nikolai". Și acum important: returnăm o altă funcție din funcție, în interiorul pe care o derivă alerta. variabil nume.. În plus, atribuim o variabilă func. Valoarea returnată din funcție salut.Și această valoare este funcția noastră care afișează un nume. acum noi salut. Noi atribuim nUL. Noi doar distrugem funcția noastră salut., totuși, când numim func.Apoi vom vedea valoarea variabilei nume.("Nikolai") funcții salut.. Cum este posibil să vă spun? Și foarte simplu. Lucrul este că funcția noastră returnată are și o proprietate. [] care se referă la domeniul de aplicare extern al vizibilității, iar acest domeniu extern în cazul nostru este un obiect Lexicalnvironment. Funcția noastră salut.. Prin urmare, în ciuda faptului că ștergem funcția noastră salut., un obiect Lexicalnvironment. Nu a fost șters și rămas în memorie și va rămâne în memorie până când este cel puțin o legătură. Avem această legătură - funcția noastră returnată care utilizează variabila nume. din această facilitate Lexicalnvironment..

Deci, hai să dăm acum definiția ce este închiderea.

Circuit - Funcția cu toate variabilele care sunt disponibile.

Ei bine, articolul sa dovedit destul de voluminos, dar acest lucru este doar pentru că am încercat să descriu întregul proces de muncă al închiderii. Pentru a vă consolida, vreau să aduc un exemplu simplu - un metru folosind subiectul tocmai studiat. Vă rugăm să vă ocupați de cod și să scrieți în comentariile, cum și de ce funcționează. Dacă nu înțelegeți ceva, puteți pune și o întrebare. Multumesc pentru atentie!

Funcție MakeCounter () ()
var actualcount \u003d 0;

Funcția de returnare () (
Currentcount ++;
Returnează codul de credit;
};
}

VAR Counter \u003d MakeCounter ();
Tejghea ();
Tejghea ();
Alertă (contra ()); // 3.

ÎN Funcții JavaScript. Poate fi descrisă nu numai una după alta, ci și una în interiorul celuilalt. Când aveți o singură funcție în altă parte, poundarea internă are acces la o funcție externă alternativă.

Funcția exterioară (x) (var tmp \u003d 3; funcția internă (y) (alertă (x + y + (+ (+ tmp)); // îndepărtează 16) internă (10);) externă (2);

Acest cod dă întotdeauna 16, deoarece funcția interioară vede x, care este o variabilă în funkucția externă. ÎN acest caz Funcția de argument. De asemenea, interne () poate vedea TMP de la extern ().

Aceasta se numește închidere sau închidere. Dacă este mai precis, închiderea se numește funcția externă și totul înăuntru se numește mediul de închidere sau mediul de închidere.

Uneori se spune că închiderea este o funcție care returnează funcția, este incorect, pentru a numi funcția de închidere că funcția internă se aplică variabilei din afara domeniului său de aplicare.

Funcția Foo (X) (VAR TMP \u003d 3; Funcția de retur (Y) (Alertă (x + Y + (++ TMP)); // va fi de asemenea alertă 16)) var (2); // Barul este acum o închidere. Bar (10);

Funcția de mai sus va afișa, de asemenea, 16, deoarece bara chiar și după terminarea foo continuă să aibă acces la X și TMP, chiar dacă variabila barului în sine nu este în interiorul domeniului de aplicare în care au fost declarate.

În același timp, deoarece variabila TMP este încă în interiorul închiderii barei, continuă să crească de fiecare dată când apelul la bar.

Aici cel mai simplu exemplu Circuit:

Var a \u003d 10; Test de funcții () (consola.log (a); // Concluzie 10 console.log (b); // concluzie 6) var b \u003d 6; Test ();

Când începeți o funcție în JavaScript, mediul este creat pentru aceasta, adică o listă a tuturor variabilelor vizibile pentru aceasta, nu numai argumentele și variabilele la declarat în interiorul acesteia, dar și în afara, în acest exemplu este "a "și" B ".

Puteți crea mai mult de o închidere într-un singur mediu, returnându-le la o matrice, obiect sau atașarea unei variabile globale. În acest caz, toți vor lucra cu toții acelasi X sau valoarea TMP fără a crea copii individuale.

Deoarece în exemplul nostru X este numărul, atunci valoarea sa copie În foo ca argument x.

Pe de altă parte, JavaScript utilizează întotdeauna legături când obiectele sunt transmise. Dacă ați chemat Foo cu un obiect ca argument, atunci închiderea returnării ar returna legătura cu obiectul original!

Funcția Foo (X) (VAR TMP \u003d 3; Funcția de retur (Y) (Alertă (x + Y + TMP); x.memb \u003d x.memb? X.memb + 1: 1; Alertă (x.memb);) ) Var vârsta \u003d numărul (2); var bar \u003d foo (vârstă); // Barul este acum o închidere referitoare la vârstă. Bar (10);

După cum era de așteptat, fiecare bară de apel (10) crește x.memb. Ceea ce nu vă așteptați, astfel încât x continuă să se refere la același obiect ca vârsta! După două apeluri de apel, vârsta.memb va fi egală cu 2! Apropo, apar scurgeri de memorie în obiecte HTML.

În programare, închiderea sau în versiunea în limba engleză "închidere" este o metodă de implementare a numelui de legare contextuală în funcțiile de primă clasă. Operativ, este o intrare care stochează funcția împreună cu mediul. Mediul este o comparație a fiecărei funcții libere cu o valoare sau o referință numită prin închiderea JavaScript-ului. Permite accesul la variabilele capturate prin copii ale valorilor sau legăturilor, chiar și atunci când sunt cauzate în afara regiunii.

Conceptul de închidere

Închiderile au fost dezvoltate în anii 1960 pentru o evaluare mecanică a expresiilor în calcul și aplicate în 1970 ca o caracteristică a limbajului de programare PAL pentru a susține funcțiile de primă clasă cu sfera lexicală. Peter Landin a definit termenul "închidere" în 1964, cu partea de mediu și de control utilizat pe aparatul second pentru a evalua expresiile lambda asociate cu un mediu lexical, care a condus la închiderea acestora sau închiderea lui JavaScript.

O astfel de explicație a fost inclusă în 1975 ca o versiune LIXI LIMP LIMP și a fost larg răspândită. Mediul lexical este o varietate de variabile valide în program. Se compune dintr-un mediu de lexic intern și referințe la un mediu extern numit variabile nonlocale.

Închiderile lexice în JavaScript sunt caracteristici cu mediul său extern. Ca și în JavaScript, toate variabilele au un link la tip. JS utilizează numai legarea prin referință - care corespunde C ++ 11, iar durata de viață a variabilelor ne-locale capturate de funcția este distribuită la durata de viață a funcției.

Circuitele din JavaScript apar, de obicei, în limbi cu valori de primă clasă. Astfel de limbi vă permit să transmiteți funcții ca argumente. Și, de asemenea, reveniți de la apelurile funcției și atașați la nume variabile. Acest lucru se întâmplă ca tipuri simple, cum ar fi șirurile și numerele întregi.

În acest exemplu, expresia lambda (Lambda (\u003e \u003d (pragul de vânzare-vânzări) apare în interiorul funcției cele mai bine vândute. Când se calculează expresia lambda, circuitul creează o închidere constând din codul de exprimare a Lambda și referința la variabila de prag, ceea ce reprezintă o variabilă liberă în expresia lambda. Închiderea este apoi transmisă de funcția de filtrare care determină în mod repetat să determine ce cărți ar trebui să fie adăugate la lista rezultatelor și care ar trebui eliminate.

Deoarece există o închidere în valoarea pragului, acesta din urmă îl poate folosi de fiecare dată când se produce filtrul. Funcția de filtrare în sine poate fi definită perfect fișier separat.. Aici este același exemplu, rescris în JS. Demonstrează modul în care închiderile sub capota în lucrarea JavaScript.

Cuvântul cheie este folosit în loc de funcția de filtrare globală, dar restul structurii și efectul codului sunt aceleași. Funcția poate crea o mai apropiată și returnează-o deoarece, în acest caz, se confruntă cu execuția funcției cu variabilele F și DX continuă să funcționeze după derivatul, chiar dacă execuția și-a lăsat domeniul de aplicare și nu mai sunt vizibile.

În limbi, fără închidere, durata de viață a variabilei locale automate coincide cu execuția cadrului de stivă, unde această variabilă este declarată. În limbile cu JavaScript, închiderile și funcțiile de viață, variabilele trebuie să continue să existe până când blocările existente au referințe la acestea. Acest lucru este cel mai adesea implementat folosind o formă de colectare a gunoiului.

Avantajul închiderii este acela că acesta păstrează domeniul de aplicare, "lanțul de vizibilitate" al unui context extern sau "parental" de execuție. Un astfel de comportament poate fi folosit în mai multe moduri și a devenit un instrument util pentru a preveni o varietate de erori JavaScript. Una dintre cele mai frecvente este problema "buclelor".

Problema cu ciclul apare atunci când utilizatorul creează o funcție în ciclu și se așteaptă ca valoarea curentă a variabilei în acest sens funcție nouăChiar dacă se schimbă în contextul ciclurilor înainte de a apela o nouă funcție. Închiderile utilizate în acest mod nu mai au transparență de referință și, prin urmare, nu mai sunt funcții curate, totuși, acestea sunt utilizate în mod obișnuit în limbi funcționale necurate, cum ar fi schema. Pentru a înțelege ce se închide în JavaScript, trebuie să luați în considerare cazurile de utilizare. De fapt, în practică, au multe aplicații:

  1. Acestea pot fi folosite pentru a determina structurile de control. De exemplu, toate structurile standard de gestionare SmallTalk, inclusiv ramurile (dacă / apoi / altceva) și ciclurile (în timp ce și pentru), sunt determinate utilizând obiecte ale căror metode iau închiderile. Utilizatorii pot utiliza cu ușurință închideri pentru a determina structura de control. În limbile care implementează scopul, puteți crea mediul multifuncțional, permițându-vă să comunicați confidențial și să schimbați acest mediu. Închiderea este utilizată pentru implementarea sistemelor de obiecte.
  2. Creând atât metode variabile private cât și cele publice utilizând șabloane de module. Datorită faptului că funcțiile returnate moștenesc zona funcției părinte, acestea sunt disponibile tuturor variabilelor și argumentelor în acest context.
  3. Este util într-o situație în care funcția folosește aceeași resursă pentru fiecare apel, dar creează și o resursă pentru el. Această circumstanță face ca metoda să fie ineficientă, care este eliminată exclusiv prin închidere.

Potrivit MDN (Mozilla Developer Network), închiderile sunt funcții cu variabile independente care "amintiți" mediul creării sale ". Și, de regulă, când funcția este finalizată, variabilele sale locale nu mai există. Înțelegeți modul în care închiderea în lucrările JavaScript, puteți lua în considerare mai multe mecanisme. Primul este o logică formală. De exemplu, aplicarea funcției de logare, care ia un nume ca parametru și îl înregistrează. Apoi, creez ciclul pentru a trece prin lista de nume, setați ora 1 de expirare, apoi sunați la funcția de logare care trece în numele curent.

În limba de primă clasă, puteți manipula același fel ca și alte tipuri de date, cum ar fi int sau șir. Numai acest mecanism permite multor lucruri incredibile, de exemplu, pentru a atribui o funcție variabilă la apelarea ulterioară sau o transmitere ca parametru al unei alte funcții.

Acest principiu este folosit de mai multe structuri, precum și de agenți de evenimente DOM. În primul rând, evenimentul este "ascultarea", apoi atribuiți o funcție de apel invers pentru a fi numită de fiecare dată când evenimentul este declanșat.

Funcția anonimă este o funcție fără un nume. Practic, programatorii novice le întâlnesc zilnic, fără a înțelege jocul cu numere. De exemplu, efectuarea operațiunii de adăugare, puteți trece prin variabile, de exemplu:

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

Sau dacă nu intenționați să reluați numerele: VAR Z \u003d 3 + 5;

Acestea sunt camere anonime. Pentru funcții anonime, le puteți declara când sunt folosite "în zbor" - fără a trece variabila. De exemplu, luați funcția de la anterior:

(Alertă ("CECI EST ONE FONCHAN Anonyme");

Mai mult decât atât, există o sintaxă alternativă a declarației unei funcții care subliniază faptul că ambele funcții pot fi anonime și se referă la variabile simple, care este o modalitate convenabilă de instalare a funcției de apel invers.

De fapt, este același mecanism, dar din acest punct de vedere vă va permite să vedeți cum este închisă funcția din interior. După cum se poate observa, deoarece funcțiile sunt variabile, ca și altele, nu există niciun motiv pentru care nu pot fi definiți la nivel local. În ordinea zero, cum ar fi C, C ++ și Java, toate funcțiile sunt determinate la un nivel de vizibilitate, în aceeași clasă sau la nivel global. Pe de altă parte, în JavaScript, funcția locală dispare, precum și alte variabile locale, de îndată ce funcția părinte se termină, deci nu este vizibilă din alte funcții.

Acest lucru este de fapt dificil, dar JavaScript are o modalitate de a urmări vizibilitatea variabilelor și chiar în două moduri. Atribuirea unei variabile globale în JavaScript are același mecanism ca și în cazul Java - obiecte complexe, matrice, elemente DOM și altele sunt transmise prin referință, astfel încât în \u200b\u200burmătorul cod:

var tab \u003d; Var tab2 \u003d fila.

În cazul în care, fila și tab2 sunt două linkuri către aceeași masă, din punct de vedere tehnic sunt indicatori controlați de colectorul de gunoi. Funcțiile sunt transmise, de asemenea, prin referință. Variabila GlobalFN nu mai este ascunsă. Ordinul vă permite să faceți ceea ce este demonstrat pe exemplul unei sarcini de închidere JavaScript.

Acesta este modul în care puteți extrage funcția din contextul local, dacă funcția satisface alte variabile locale. Un exemplu simplu: auto-increment, o funcție care returnează un număr întreg care crește cu 1 fiecare apel de timp. În mod specific, nevoile funcției INC, care se comportă după cum urmează:

// retourne 0 inc ();

// retourne 1 Inc ();

// retourne 2 Inc ();

Cu închiderea se pare că:

funcție MakeIc () (var x \u003d 0; funcția de retur () (retur x ++;)) var inc \u003d ainci ();

În ultimul rând în momentul în care variabila Inc este creată, acesta are unele variabile care sunt în jur, în acest caz x. Creează un anumit obiect invizibil în jurul funcției care conține această variabilă. Acest obiect este o funcție de închidere JavaScript. În același timp, fiecare copie a funcției va avea propria sa închidere:

var inc1 \u003d makeinc ();

var inc2 \u003d makeinc ();

După cum se poate vedea, închiderea este foarte utilă în multe cazuri.

Pentru a evita conflictele de nume variabile, spațiul de nume se utilizează de obicei. În JavaScript, spațiile de nume sunt obiecte ca oricare altul.

În mod natural, A.X și B.X nu este aceeași variabilă. Cu toate acestea, dacă trebuie doar să porniți scriptul, fără a necesita variabile de salvare pentru restul, o funcție anonimă poate fi utilizată ca o închidere. Aceasta oferă o sintaxă ușor ciudată. Deși cele două coduri din mijloc sunt destul de comune, pe de altă parte, funcția care este în jur este efectuată "în zbor". Acordați atenție parantezelor () la sfârșit. Și pentru a putea face o închidere, funcție anonimă El însuși trebuie să fie înconjurată de paranteze rotunde.

În această funcție anonimă folosește o variabilă locală, paragraf. Aceasta este o modalitate excelentă de a preveni numele conflicte sau clocsiness, dar și împotriva ataks XSS. Variabilele personalizate sunt protejate, nimeni nu le poate schimba pentru a afecta comportamentul scriptului.

Există o opțiune: (funcție () (// ...) ());

În același timp, atrageți atenția asupra permutării parantezelor. Diferența dintre aceste două opțiuni este destul de dificil de explicat deoarece sunt asociate cu modul în care codul este citit de un analizor lexical. În ambele cazuri, funcția este considerată o expresie, dar această expresie nu este estimată simultan. Trebuie doar să vă amintiți că el ia două perechi de paranteze rotunde: una în jurul funcției și una în spatele ei.

Programarea JavaScript în cicluri

Când utilizatorul efectuează volume mari de programare JavaScript, este dificil pentru a evita ciclurile. Cineva este nebun, după care ei ajung la ideea că orice implementare a lui JavaScript are o greșeală serioasă. Dacă dezvoltatorul are deja un ciclu pe care nu dorește să le convertească pentru a folosi funcția iteratorului, tot ceea ce trebuie să facă este o închidere în care definește noi variabile. Acestea fixează valoarea curentă a variabilelor și variază pe fiecare iterație. Trucul de capturare a variabilelor este că închiderea externă se efectuează imediat în timpul repetării actuale a ciclului. Puteți utiliza una dintre aceste două abordări aproximative.

Acum există o altă soluție simplificată la această problemă, deoarece cuvântul cheie este susținut atât în \u200b\u200bFirefox, cât și în Chrome. Este cuvânt cheie. În loc de bloc variabilă variabilă. Lăsați să funcționeze magic, deoarece este declarată o nouă variabilă J, valoarea a căror valoare este fixată cu închiderea în interiorul ciclului. Cu toate acestea, trebuie să se țină cont de faptul că nu continuă să existe după încheierea aceleiași iterații a ciclului, deoarece este local.

Buclă și funcție

Pentru o buclă din JavaScript nu apare, precum și pentru un ciclu în C sau Java. De fapt, arată mai mult ca PHP. Cele mai importante cunoștințe despre ciclurile din JS este că acestea nu creează o zonă de acțiune. JS nu are un bloc de sferă, numai funcția volumului. Această proprietate poate fi luată în considerare pe următorul fragment:

fUNCȚIA FOO () (VAR BAR \u003d 1;

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

Este clar că bara este disponibilă în întreaga funcție. Înainte de prima iterație a ciclului BZ va fi nedefinită. După ciclu, va avea o valoare de 41 (și voi fi 42). Astfel, orice variabilă declarată oriunde în funcție va fi disponibilă peste tot în funcție și va fi valabilă numai după ce a fost numită.

Obloane și agregare

Închiderea nu este nimic mai mult decât funcții, în interiorul alte funcții și sunt transmise unui alt context. Ele sunt numite închidere, în timp ce se apropie prin variabilele locale, adică accesibile altor zone ale sferei. De exemplu, timpul, x definit ca parametru FOO și VAR Bar \u003d Foo (2) () va returna 84.

Funcția Foo returnată are acces X. Acest lucru este important deoarece ajută dezvoltatorii să creeze funcții în cicluri în funcție de variabilele ciclului. Luați în considerare acest fragment, care atribuie procesor la diferite elemente:

// elemente este o matrice de 3 elemente Dom Var valori \u003d ["foo", "bar", "baz"];

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

elemente [i] .onclick \u003d funcția () (Alertă (date);

Valoarea pe care o vor folosi alertă atunci când vor apărea, vor fi aceleași pentru toată lumea, și anume Baz. În acel moment, manipulatorul evenimentului este chemat, pentru că deja finalizat. JS nu are o zonă bloc, adică. Toți manipulați folosesc legătura cu aceeași variabilă de date. După buclă, această valoare va fi valori. Fiecare declarație variabilă creează un loc în memoria de stocare. Pentru că aceste date sunt modificate din nou și din nou, poziția în memorie rămâne neschimbată.

Fiecare manipulator de evenimente are acces la aceeași poziție în memorie. Singura soluție este aceea de a intra într-o altă zonă care "rezolvă" valoarea curentă a datelor. JS are doar o serie de funcții. Prin urmare, este introdusă o altă funcție. Exemplu:

funcție CreareEventHandler (x) (funcția de retur () (Alertă (x);

pentru (var i \u003d 0, l \u003d elemente.Length;

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

elemente [i] .onclick \u003d createEventHandler (date);

Funcționează, deoarece valoarea datelor va fi stocată în zona locală, createEventHandler și această funcție se efectuează pe fiecare iterație. Acesta poate fi scris pe scurt, folosind funcțiile executabile imediat:

pentru (var i \u003d 0, l \u003d elemente.Length;

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

elemente [i] .onclick \u003d (funcția (x) (funcție () (alertă (x);

Exemplu practic de închidere în JavaScript

Dacă utilizatorul se închide chiar deasupra codului din browser, se poate întâmpina o problemă, deoarece poate face o eroare de sintaxă. Dacă execută codul direct în browser, șansele sunt foarte mari să nu compileze procesul de compilare a Webpack-ului. Solutii posibile:

funcția de lucru (nume) (

funcția de returnare (subiect) (

consola.log (ceea ce este $ (subiect) în $ (nume));

lucrare ("JavaScript") ("închidere");

În primul rând, funcția este numită și argumentul de nume este transmis. Acum, această funcție de vocabular returnează, de asemenea, o funcție care acceptă și argumentul subiectelor. Această caracteristică înregistrează ieșirea, iar ieșirea are acces la variabilă.

Zona de funcții Insider nu se limitează la această funcție, astfel încât conceptul se numește închidere, deoarece are acces la această zonă parametrul extern. Funcția returnată are acces la zona lexicală externă sau contexte. Când dezvoltatorul numește o funcție care o returnează, apoi a sunat mai întâi funcție variabilă Disponibil întotdeauna pentru funcția internă. Exemplu suplimentar cu următorul cod.

Un exemplu de funcție internă

Citiți mai multe despre închiderea în JavaScript poate fi informată cu privire la al doilea exemplu. Acum, acest mediu de împlinire este distrus, dar numele parametrului există încă. Se creează un nou mediu funcțional intern, care este o funcție anonimă. Are acces la zona mediului lexical exterior.

Astfel, într-o variabilă a mediului exterior, ea există încă în așa fel încât o funcție anonimă să aibă acces la imprimările variabile ale numelui din consola, de exemplu, "ceea ce este o închidere în JavaScript". Funcția anonimă internă //main.js

funcție Funcție () (produse VAR \u003d;

i ++) (produse.push (funcție () (consola.log (i);

) Produse de returnare;

) Var sapun \u003d fabrică ();

Rezultatul acestui exemplu este destul de nesemnificativ și egal cu 2.

Când săpunul este săpun () numit o variabilă de context externă, întotdeauna 2, deoarece în ciclu, starea este falsă în i<2, поэтому при этом значение i равно 2, а во время вызова нужно напечатать значение в консоль так, она всегда пишет 2. То же самое для мыла - soap ().

Crearea funcțiilor "în zbor"

Puteți crea o fabrică de funcții - funcționaleFactory care efectuează sarcini personalizate. Funcția rezultată din fabrica de funcții va fi o închidere, un mediu de stocare de creare.

var FunctionFactory \u003d FUNCTION (NUM1) (retur num1 * Num2;

Cele de mai sus vă permit să transferați un număr de funcționare. Apoi, funcționarea returnă o închidere, amintindu-vă valoarea Num1. Funcția rezultată înmulțește numărul original Num1 de ori valoarea numitului, care este transmisă atunci când este apelată.

var mult5 \u003d funcționare (5);

var mult10 \u003d funcționalFactory (10);

Cele de mai sus creează pur și simplu funcțiile mult4 și mult10. Acum vă puteți referi la oricare dintre aceste funcții prin trecerea unui număr nou pentru a multiplica cu 5 sau 10. Acum puteți vedea rezultatul.

Închiderea este una dintre cele mai puternice funcții JavaScript, dar nu poate fi folosit corect fără a înțelege esența. Acestea sunt relativ ușor de creat întâmplător, acesta este modul în care închiderea periculoasă JavaScript. Crearea lor are efecte potențial dăunătoare, în special în unele medii de browser web relativ comune. Pentru a evita o coliziune aleatorie cu dezavantaje și pentru a profita de avantajele pe care le oferă, este necesar să înțelegem mecanismul lor.

În acest articol, voi încerca să explic zonele de vizibilitate și închidere în JavaScript, în care multe dificultăți de experiență.

Introducere

Există câteva articole în rețea care încearcă să explice zonele de vizibilitate și închidere, dar în general, aș spune că majoritatea nu sunt în întregime de înțeles. În plus, în unele articole se presupune că sunteți programat înainte de alte 15 limbi, deși cred că majoritatea oamenilor care scriu pe JavaScript au doar experiență în HTML și CSS și nu în C sau Java.

În consecință, scopul acestui articol este de a explica pentru toate - care este domeniul de vizibilitate și închiderea, așa cum funcționează și, cel mai important, avantajul lor. Înainte de a citi acest articol, trebuie să cunoașteți conceptele de bază despre variabilele și funcțiile din JavaScript.

Zona de vizibilitate

Domeniul de aplicare înseamnă unde sunt disponibile variabile și funcții și în ce context sunt executate. O variabilă sau o funcție poate fi definită într-o zonă globală sau locală de vizibilitate. Variabilele au așa-numita funcție de vizibilitate a funcției și funcțiile au același domeniu ca variabilele.

Domeniu global

Când ceva este global, înseamnă că este disponibil de oriunde din codul dvs. Luați în considerare un exemplu:

var maimuță \u003d "gorila"; Funcția greetvisitor () (Alertă de întoarcere ("Bună ziua Dragă Blog Reader!");)

Dacă acest cod a fost efectuat într-un browser web, zona de vizibilitate ar fi fereastră, va fi disponibilă pentru tot ceea ce este executat în fereastră.

Domeniul de aplicare local

Spre deosebire de zona de vizibilitate globală, zona de vizibilitate locală este atunci când este definită și disponibilă numai în unele părți a codului, cum ar fi o funcție. Luați în considerare un exemplu:

funcția TalkDirty () (VAR spune \u003d "Oh, tu puțin iubit VB, tu"; întoarcerea alertă (spunând);) Alertă (spunând); // aruncat o eroare

În acest exemplu, variabila de zicală este disponibilă numai în interiorul funcției de convorbire, în afara căreia nu este definită. Notă: Dacă ați fost identificați prin a spune fără un cuvânt cheie VAR, acesta ar deveni automat global.

În plus, dacă aveți funcții imbricate, funcția internă va avea acces la funcțiile în care este încorporată, precum și variabilele:

funcția capitalizename (retur Firstname.tuppercase ();) var capitalizat \u003d capitalizename (); retur capitalizat;) Alertă (Savenme ("Robert")); // întoarce "robert"

După cum ați văzut, funcția internă Capitalizename nu trebuie să transmită niciun parametrii, deoarece Are acces complet la parametrul Primului nume în funcția externă a sapamei. Pentru o mai mare claritate, luați în considerare un alt exemplu:

fUNCTION FIBLINGS (SIBLINGS \u003d ["John", "Liza", "Peter"]; Funcția SiblingCount () (VAR SIBLINGHT \u003d SIBLINGS.LINGTH; RETURNING Lungime;) Funcție JoinsiblingNames () (Return "Am" + SiblingCount () + "SIBLINGS: NN" + SIBLINGS.Join ("n");) Returnați JoinsiblingNames ();) Alertă (frați ()); // ieșiri "Am 3 frați: John Liza Peter"

După cum puteți vedea, ambele funcții interne au acces la matricea SIBLINGS și fiecare funcție internă are acces la o altă funcție internă de același nivel (în acest caz, JoinsiblingNames are acces la SiblingCount). Cu toate acestea, variabila de lungime de siblings în interiorul SiblingCount este disponibilă numai în interiorul acestei funcții, adică În acest domeniu de vizibilitate.

Circuit

Acum că aveți o idee mai clară despre zonele de vizibilitate, vom adăuga închiderea acestora. Circuitele sunt expresii, de obicei funcții care pot funcționa cu un set de variabile într-un anumit context. Sau, cuvinte mai simple, funcții interne care se referă la variabilele locale ale funcțiilor externe formează închiderea. De exemplu:

funcție Adăugați (x) (Return x + y;);)) VAR ADD5 \u003d Adăugați (5); var n08 \u003d add5 (3); Alertă (NO8); // returnează 8.

Blimey! Ce se petrece aici? Să ne dăm seama:

1. Când numim funcția Adăugare, acesta returnează funcția.

2. Această funcție închide contextul și memorează modul în care parametrul X este în acest moment (adică, în acest caz, valoarea 5)

3. Când rezultatul funcției Adăugare este atribuit variabila ADD5, va ști întotdeauna ce a fost atunci când creați această variabilă.

4. Adăugarea variabilă se referă la o funcție care va adăuga întotdeauna valoarea 5 la orice argument transmis.

5. Aceasta înseamnă că atunci când numim Add5 cu valoarea 3, acesta va plula numărul 5 și 3 și va returna 8.

De fapt, în lumea JavaScript, funcția ADD5 arată astfel:

funcția ADD5 (Y) (Return 5 + Y;)

Problema notorie a ciclurilor
De câte ori ați creat cicluri în care ați vrut să atribuiți o valoare de I în vreun fel, de exemplu, un element și ați înțeles că numai ultima valoare am fost returnată?

Gresitrecurs

Să ne uităm la acest cod incorect care creează 5 elemente , adaugă valoarea I ca text pentru fiecare element și onclick, care este de așteptat să producă alertă cu valoarea I pentru această referință, adică. Aceeași valoare ca în textul elementului. Apoi elementele sunt adăugate la corpul documentului:

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

Fiecare element conține textul corect, adică "Link 0", "Link 1", etc. Dar indiferent de link-ul pe care l-ați făcut clic, acesta arată alertă cu un număr 5. Care este problema? Motivul este că valoarea variabilei i crește cu 1 cu fiecare iterație a ciclului și pentru că Evenimentul OnClick nu este executat, ci se aplică pur și simplu elementului Valoarea crește.

Prin urmare, ciclul continuă să lucreze până când voi deveni egal cu 5, care este ultima valoare înainte de a ieși din funcția de addlinks. Mai mult, de fiecare dată când evenimentul onClick este declanșat, ultima valoare I este luată.

Recursul potrivit

Ceea ce trebuie să faceți este să creați o închidere. Ca rezultat, când aplicați valoarea I la un eveniment de eveniment onClick , I-ar fi atribuit valoarea lui I în acel moment. De exemplu, astfel:

funcție ADDDINKS () (pentru (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;

Folosind acest cod, dacă faceți clic pe primul element, Alert va emite "0", pe al doilea - "1" etc. Soluția este că funcția internă aplicată la evenimentul OnClick creează o închidere în care apelurile numerelor numerelor, adică la valoarea lui I în acel moment.

Această caracteristică "își amintește" valoarea dorită și poate returna cifra corespunzătoare atunci când este declanșat evenimentul OnClick.

Funcții de executare securizată

Funcțiile de executare în condiții de siguranță sunt astfel de funcții care încep să execute imediat și să creeze închiderea lor. Luați în considerare un exemplu:

(Funcție () (câmpul VAR \u003d "păstorul german"; alertă (câine);)) (); Alertă (câine); // returnează nedefinit.

Deci, variabila de câine este disponibilă numai în acest context. Gândiți-vă, câinele variabil ascuns ... dar prietenii mei, cel mai interesant lucru începe cu asta! A rezolvat problema noastră cu un ciclu și este, de asemenea, baza pentru modelul modulului Yahoo JavaScript.

Modelul modulului Yahoo JavaScript

Esența acestui model este că utilizează o funcție executabilă în siguranță pentru a crea o închidere, prin urmare face posibilă utilizarea proprietăților și a metodelor private și publice. Simplu exemplu:

vAR PERSON \u003d FUNCTION () (// Numele Var Private \u003d "Robert"; Return (GetName: Funcție () (Nume de retur), SetName: Funcție (NEWNAME) (NAME \u003d NEWNAME;));) (); alertă (persoană.name); // alertă nedefinită (persoană.GetName ()); // "robert" persoană.setname ("robert nyman"); alertă (persoană.GetName ()); // "Robert Nyman"

Avantajul acestei abordări este că vă puteți defini, care va fi deschis în obiectul dvs. (și poate fi schimbat) și că este închis, pe care nimeni nu îl poate contacta sau schimba. Variabila de nume este ascunsă în afara contextului funcției, dar este accesibilă funcțiilor GetName și SetName, deoarece Ele creează închideri în care există o legătură cu variabila de nume.

Concluzie

Sper sincer că după citirea acestui articol, noii veniți și programatori experimentați au primit o idee mai clară despre modul în care în zonele de lucru JavaScript de vizibilitate și închidere. Întrebările și recenziile sunt binevenite și dacă aveți ceva important pentru a lăsa ceva, atunci pot actualiza articolul.

Bună codificare!

Circuite în JavaScript. Folosit pentru a ascunde valorile variabile și a stoca funcții. Punctul este că atunci când este închis, se creează o funcție în care sunt specificate variabilele și care, ca rezultat al funcționării, returnează funcția imbricată. Apoi, în el (în funcția principală), se creează o funcție încorporată, în care unele operații sunt realizate cu variabile de funcție variabilă și care returnează rezultatul acestor operații. Apoi, funcția principală este egală cu o variabilă - această variabilă poate fi apelată cât timp și totuși, valorile variabilelor funcției principale vor fi stocate și actualizate. Ea este "închisă".

După cum știți, în JavaScript câmpul de vizibilitate a variabilelor locale (cuvânt VAR declarat) Este corpul funcției în care sunt definite.

Dacă declarați o funcție în interiorul unei alte funcții, primele primesc acces la variabile și argumente ale acestora:

Cod: Funcție Outerfn (MyARG) (
Var myvar;
Funcție INNERFN () (
// are acces la Myvar și Myarg
}
}

În același timp, astfel de variabile continuă să existe și să rămână funcții interne disponibile chiar și după ce funcția externă în care sunt definiți a fost executată.

Luați în considerare un exemplu - o funcție care returnează numărul de valori proprii:

Cod: Funcție CreareCounter () (
VAR NUMBEROFCALLS \u003d 0;
Funcția de returnare () (
Returnați ++ numerelor;
}
}
var fn \u003d createCounter ();
fn (); //unu
fn (); // 2.
fn (); // 3.

În acest exemplu, funcția returnată de CreateCounter utilizează variabila numerelor, care salvează valoarea dorită între apelurile sale (în loc să-și oprească imediat existența cu întoarcerea CreateCounter).

Este vorba despre aceste proprietăți că astfel de funcții "imbricate" în JavaScript sunt numite închideri (termenul care provin din limbi funcționale de programare) - ele "mai aproape" variabilele și argumentele funcției în care sunt definite.

Aplicarea închiderilor

Simplifică un mic exemplu de mai sus - eliminați necesitatea de a apela separat funcția CreateCounter, făcându-l annexic și îl apel imediat după anunțul său:

Cod: var fn \u003d (funcția () (
VAR NUMBEROFCALLS \u003d 0;
Funcția de returnare () (
Returnați ++ numerelor;
}
})();

Un astfel de design ne-a permis să lege datele la funcție, ceea ce a rămas între provocările sale este una dintre utilizările de închidere. Cu alte cuvinte, cu ajutorul acestora putem crea funcții care au o stare variabilă.

Alte utilizare bună a închiderilor - crearea de funcții, la rândul său, creând și funcții - ceea ce unii vor fi numiți T.N. Metaprograming.
De exemplu:

Cod: Var CreateHellFunction \u003d Funcție (Nume) (
Funcția de returnare () (
Alertă ("salut," + nume);
}
}
varyhellohabarahabbr \u003d createhellfunction ("habrahbr");
sayhellohabrahabbr (); // alerte "salut, habrahbr"

Datorită închiderii, funcția returnată "își amintește" parametrii transferați la funcțiile de a crea că avem nevoie de astfel de lucruri.

O situație similară apare atunci când nu returnăm o funcție internă și atârnă de orice eveniment - deoarece evenimentul apare după ce funcția a fost îndeplinită, închiderea din nou nu va pierde datele transmise atunci când se creează procedura de date.

Luați în considerare un exemplu ușor mai complex - o metodă care leagă funcția la un context specific (adică obiectul la care acest cuvânt va indica în ea).

Cod: funcții.protype.bind \u003d funcție (context) (
var fn \u003d acest lucru;
Funcția de returnare () (
Returnați fn.Apply (context, argumente);
};
}
vAR HELPAGE \u003d (
Nume: "Habrahbr",
Init: Funcție () ()
Alertă ("Bună ziua" + acest nume);
}
}
//Window.onload \u003d hellopage.init; // Alertnul ar fi nedefinit, pentru că Aceasta indică fereastra
fereastră.Ocload \u003d hellopage.init.bind (Hellopage); // Acum totul funcționează

În acest exemplu, cu ajutorul unei închideri, o funcție, o funcție declanșată "ohm, își amintește funcția inițială și contextul atribuit acestuia.

Următoare, utilizarea fundamental diferită a închiderilor - protecția datelor (încapsulare). Luați în considerare următorul design:

Cod: (Funcție () (

})();

Evident, în interiorul închiderii avem acces la toate datele externe, dar are propria sa. Datorită acestui lucru, putem înconjura o parte a codului ca un design pentru a închide variabilele locale de la accesul exterior. (Un exemplu de utilizare a acesteia poate fi văzut în codul sursă al Bibliotecii JQuery, care înconjoară închiderea întregului cod astfel încât să nu prezinte variabilele numai la acesta).

Există totuși unul asociat cu o astfel de capcană de utilizare - sensul cuvântului acest lucru este pierdut în interiorul închiderii. Acesta este rezolvat după cum urmează:

Cod: (Funcție () (
// cea mai mare acest lucru va continua
). Apelați (acesta);

Luați în considerare o altă recepție din aceeași serie. Acesta a fost popularizat pe scară largă de către dezvoltatorii de cadru Yahoo UI, numindu-i "modelul modulului" și scrie un întreg articol pe acesta într-un blog oficial.
Să avem un obiect (Singleton) care conține orice metode și proprietăți:

Cod: Var MyModule \u003d (
Nume: "Habrahbr",
A spus: funcția (numele) (
Alertă ("predicată" + nume.touppercase ())
},
Acest lucru.Saypresed (acest nume);
}
}
Mymodule.saypresatedohabarthabar ();

Cu ajutorul închiderii, putem face metode și proprietăți care nu sunt utilizate în afara obiectului, private (adică disponibil numai pentru el):

Cod: Var MyModule \u003d (funcția () (
Var nume \u003d "habrahbr";
Funcția declarată () (
Alertă ("predicată" + nume.touppercase ());
}
Întoarcere (
SaypresTohabartHabar: Funcția () ()
A spus (nume);
}
}
})();
Mymodule.saypresatedohabarthabar (); // alerte "prevede Habrahbr"

În cele din urmă, vreau să descriu o greșeală comună pe care multe călătorește într-o stupoare în caz de ignoranță a modului în care lucrează închiderile.

Să avem o gamă de link-uri, iar sarcina noastră este să o facem astfel încât atunci când faceți clic pe fiecare unler, numărul de secvență este afișat.
Prima decizie care vine în minte arată astfel:

Cod: pentru (var i \u003d 0; i< links.length; i++) {
Alertă (i);
}
}

De fapt, se pare că atunci când faceți clic pe orice legătură, este afișat același număr - valoarea legăturilor. Lungime. De ce se întâmplă asta? În legătură cu închiderea, variabila auxiliară anunțată continuă să existe, cu și în momentul în care facem clic pe link. De când, în acel moment, ciclul a trecut deja, rămân egal cu numărul de linkuri - această valoare pe care suntem vizibili atunci când faceți clic.

Această problemă este rezolvată după cum urmează:

Cod: pentru (var i \u003d 0; i< links.length; i++) {
(Funcție (I) (
Link-uri [i] .onclick \u003d Funcție () ()
Alertă (i);
}
) (i);
}

Aici, cu ajutorul unei alte închideri, "umbra" variabilă i, creând o copie în zona locală de vizibilitate la fiecare pas de ciclu. Datorită acestui lucru, totul funcționează acum ca fiind conceput.

Asta e tot. Acest articol, desigur, nu pretinde că este exhaustiv, dar cineva, sper, încă ajută să-i dau seama.

zy.
Pentru economisirea între apeluri mai ușor de utilizat FUNC_NAME.ATTR Tip:

Cod: Număr de funcții (resetare) (
dacă (resetați ||! numără.cnt) numărăt.cnt \u003d 0;
retur numit.cnt ++;