Zatvaranje JS-a. Krugovi u Javascript-u: Praktični primjer, funkcije i pravila

Zdravo svima! U ovom ćemo članku pogledati Što je zatvaranje u JavaScript-u.

Ovo je prilično jednostavna tema, ali zahtijeva razumijevanje. Prvo pogledajmo šta se događa unutar funkcije.

Funkcija pozdrava (ime) (
// lexicalenvironment \u003d (Ime: "nikolai", tekst: nedefinirano)
var tekst \u003d "zdravo" + naziv;
// lexicalvironment \u003d (Ime: "Nikolai", tekst: "Zdravo, nikolai")
Upozorenje (tekst);
}

Pozdrav ("nikolai");

Šta se ovdje događa i šta je LexicaLenvironment? Hajde da to shvatimo.

Kad se funkcija naziva, stvara objekt LexicaLenvironmentU kojem se bilježe sve lokalne varijable i funkcije, kao i referenca na vanjski opseg (o tome kasnije). U našem slučaju imamo lokalnu varijablu ime.koji odmah ima vrijednost (tada prolazimo) i ovaj "nikolai". U jednom od članaka već sam napisao, međutim, podsjećam da tumač zna sve o svim varijablama unaprijed. Radi se o tome da na samom početku funkcije već ima varijablu tekst., prevodilac zna za to, ali pošto još nismo dostigli zadatak ove varijable neke vrste, jednak je nedefinirano.. Sada dodijelimo varijabilnu vrijednost i naš objekt LexicaLenvironment Promjene. Njegova imovina tekst. To postaje jednaka činjenici da smo snimili ("Zdravo, Nikolaj" u našem slučaju). Nakon što se funkcija razradila, objekt LexicaLenvironment Uništiti. Sa narednim pozivima, on će se stvoriti ponovo itd.

Sada se okrećemo u sljedećem primjeru. Recite mi šta će biti izvedeno u ovom slučaju?

Var B \u003d 2;
Funkcija x (a) (
upozorenje (A + B);
}
x (1);

Mislio? Mislim da će se najviše odgovoriti da će se prikazati broj 3, a ovo je pravi odgovor, ali možete vam reći kako je prevodilac naučio o varijabli b.? Uostalom, nije u funkciji tijela. Ako ne, razradimo.

Zapravo unutra javaScript. Postoji skrivena imovina koja se zove [] . Kada se funkcija deklarira, ona se uvijek negdje proglašava. Ova značajka može biti u drugoj funkciji, može biti u globalnom objektu itd. U našem slučaju, funkcija se deklarira u globalnom objektu. prozor., dakle, imovina x. [] \u003d prozor.

Var B \u003d 2;
Funkcija x (a) (// x. [] \u003d Prozor
// lexicalvenvironment \u003d (a: 1) -\u003e prozor
upozorenje (A + B);
}
x (1);

Ova strelica ima predmet LexicaLenvironment - To je referenca na vanjski opseg, a ova veza uspostavljena imovina. [] . Tako u objektu LexicaLenvironment Imat ćemo referencu na vanjski objekt. prozor.. Kad prevodilac traži varijablu, a zatim prvo pretražuje u objektu LexicaLenvironmentZatim, ako nije pronašao varijablu, onda izgleda u vezi, prelazi u vanjski opseg i pretražuje ga tamo i tako do kraja. Ako nije našao ovu varijablu bilo gdje, to će biti greška. U našem slučaju, varijabla sVEDOK JOVANOVIĆ - ODGOVOR: Prevodilac će uzeti iz objekta LexicaLenvironmenti varijabla b. Iz objekta prozor.. Naravno, ako imamo lokalnu varijablu b. Uz neku vrijednost, bit će zabilježen u objektu LexicaLenvironment I naknadno će biti uzet odatle, a ne iz vanjskog polja vidljivosti.

BITAN! Sjetite se tog imanja [] Instaliran je mesto gde je deklarirana funkcija, a ne nastala, zato se kod dolje prikazuje broj 3, a ne 5, kao što neki mogu misliti.

Bar B \u003d 2;
Funkcija x (a) (
upozorenje (A + B);
}

Funkcija Y () (
Var B \u003d 4;
x (1);
}

Sve je uvod samo da bi shvatio kako to sve radi, i bilo je lakše da shvatite kako zatvarači rade. A sada se direktno okrećemo temi članka.

Kao što rekoh, objekt LexicaLenvironment Svaki put se uništava svaki put nakon izvršenja funkcije i kreira se ponovo prilikom ponovnog poziva. Međutim, šta ako želimo sačuvati ove podatke? Oni. Želimo da se sve zabilježi LexicaLenvironment Sačuvan je i koristio se za sljedeće izazove? To je za to zatvaranje.

Funkcija pozdrava (ime) (
// lexicalenvironment \u003d (Ime: "Nikolai")
Povratna funkcija () (// [] \u003d lexicaLunMironment
Upozorenje (ime);
};
}

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

Da vidimo šta smo uradili. Prvo kreiramo funkciju pozdrav.u kojem se imenova ime. Funkcija stvara objekt LexicaLenvironmentgde se kreira nekretnina (naša lokalna varijabla) ime. A ona je dodijeljena imenu "Nikolaj". I sada važno: vraćamo drugu funkciju iz funkcije, unutar kojeg proizimo kroz upozorenje. varijabla ime.. Nadalje dodijelimo varijablu func. Vrijednost vraćena iz funkcije pozdrav.A ova vrijednost je naša funkcija koja prikazuje ime. sad mi pozdrav. Dodijelimo null. Upravo uništavamo našu funkciju pozdrav., međutim, kad nazovemo func.Tada ćemo vidjeti vrijednost varijable ime.Funkcije ("nikolai") pozdrav.. Kako je moguće reći? I vrlo jednostavno. Stvar je da naša vraćena funkcija također ima svojstvo. [] što se odnosi na vanjski opseg vidljivosti, a ovaj vanjski opseg u našem slučaju je objekt LexicaLenvironment Naša funkcija pozdrav.. Stoga, uprkos činjenici da izbrišemo našu funkciju pozdrav., objekt LexicaLenvironment Nije izbrisano i ostalo u memoriji, a ostat će u memoriji dok ne bude barem jedna veza. Imamo ovu vezu - našu vraćenu funkciju koja koristi varijablu ime. ovog objekta LexicaLenvironment.

Dakle, sada dajemo definiciju Šta se zatvara.

Krug - Funkcija sa svim varijablama koje su mu dostupne.

Pa, članka je pokazao prilično obiman, ali to je samo zato što sam pokušao opisati cijeli proces rada zatvaranja. Za konsolidaciju, želim donijeti jednostavan primjer - metar koristeći temu upravo proučavan. Molimo vas da se bavite kodom i pišite u komentarima, kako i zašto djeluje. Ako nešto ne razumijete, možete postaviti i pitanje. Hvala na pažnji!

Funkcija Makecounter () (
var cutrentcount \u003d 0;

Povratna funkcija () (
CurrentCount ++;
Povratni struji;
};
}

Var counter \u003d makecounter ();
Brojač ();
Brojač ();
Upozorenje (brojač ()); // 3.

U JavaScript funkcije Može se opisati ne samo za drugim, već i jedan unutar drugog. Kada imate jednu funkciju unutar druge, unutarnja funkcija ima pristup naizmjeničnoj vanjskoj funkciji.

Funkcija Vanjska (X) (VAR TMP \u003d 3; Funkcija Interna (Y) (upozorenje (X + Y + (++ TMP)); // Uklanja 16) Interna (2);

Ovaj kôd uvijek daje 16, jer unutrašnja funkcija vidi x, što je varijabla na vanjskom funkciji. U ovaj slučaj Argument funkcija. Također interni () može vidjeti TMP od vanjskog ().

To se naziva zatvaranjem ili zatvaranjem. Ako se tačnije zatvaranje naziva vanjska funkcija, a sve se unutar njega naziva okolišno zatvaranje ili okoliš zatvarača.

Ponekad se kaže da je zatvaranje funkcija koja vraća funkciju, pogrešno je, kako bi se funkcija zatvorenog naznačila da se unutarnja funkcija odnosi na varijablu na varijabli iz vanjske strane svog djelokruga.

Funkcija foo (x) (var tmp \u003d 3; povratna funkcija (y) (upozorenje (X + y + (++ tmp)); // također će upozoriti 16)) var bar \u003d foo (2); // bar je sada zatvarač. Bar (10);

Gore navedena funkcija također će se prikazati 16, jer je Bar čak i nakon završetka Foo-a i dalje ima pristup X i TMP, čak i ako se sama šipka nije u okviru u kojem su proglašeni.

Istovremeno, budući da je TMP varijabla još uvijek unutar zatvarača bara, nastavlja se povećavati svaki put kada svaki put pošiljka.

Ovdje najjednostavniji primjer Krug:

Var a \u003d 10; Funkcijski test () (konzola.log (a); // Zaključak 10 konzola.log (b); // Zaključak 6) var b \u003d 6; test ();

Kada pokrenete funkciju u JavaScript, okruženje je stvoreno za njega, odnosno popis svih varijable vidljivih na njega, ne samo argumenti i varijable proglašeni unutar nje, već i izvana, u ovom primjeru je "a "i" B ".

Možete stvoriti više zatvaranja u jednom okruženju, vraćajući ih u niz, predmet ili pričvršćivanje na globalnu varijablu. U ovom slučaju će svi raditi sa isti jedan X ili TMP vrijednost bez stvaranja pojedinih primjeraka.

Budući da je u našem primjeru x broj, onda njegova vrijednost kopija U foo kao njegov argument x.

S druge strane, JavaScript uvijek koristi veze kada se prenose predmeti. Ako ste nazvali foo s objektom kao argumentom, tada bi povratna zatvarača vratila vezu na izvorni objekt!

Funkcija foo (x) (var tmp \u003d 3; povratna funkcija (y) (upozorenje (X + y + tmp); x.memb \u003d x.memb? X.memb + 1: 1; upozorenje (x.memb);) ) Var dob \u003d broj (2); var bar \u003d foo (dob); // Bar je sada zatvaranje koje se odnosi na starost. Bar (10);

Kao što se očekivalo, svaki pozivni bar (10) povećava X.MEMB. Što možda ne biste očekivali, tako da se X i dalje odnosi na isti objekt kao i starost! Nakon dva poziva Bar, Starost.Memb bit će jednak 2! Usput, dostignu se curenja memorije u HTML objektima.

U programiranju, zatvaranjem ili u verziji engleskog jezika "Zatvaranje" je metoda za implementaciju kontekstualnog obvezujućeg imena u prvim klasnim funkcijama. Operativno je to unos koji pohranjuje funkciju zajedno sa medijom. Okoliš je usporedba svake slobodne funkcije s vrijednošću ili referencom koja se zove zatvaranje JavaScripta. Omogućuje pristup snimljenim varijablama kroz kopije vrijednosti ili veza, čak i kada su uzrokovane izvan regije.

Koncept zatvaranja

Zatvaranje su razvijene u 1960-ima za mehaničku procjenu izraza u izračunu i primjenjivali su 1970. kao značajku programskog jezika PAL-a za podršku prvoklasnim funkcijama s leksičkom sferom. Peter Landin definirao je pojam "zatvaranje" 1964. godine sa srednjim i kontrolnim dijelom koji se koristio na Secd mašini za procjenu lambda izraza povezanih s leksičkim okruženjem, što je dovelo do zatvaranja ili zatvaranja JavaScripta.

Takvo objašnjenje bilo je uključeno 1975. kao leksički ograničena liska verzija i bila je rasprostranjena. Leksičko okruženje je različite važeće varijable u programu. Sastoji se od unutrašnjeg leksičkog medija i reference na vanjsko okruženje koje se zove nelokalne varijable.

Otpornost na tjelesno zatvaranje u JavaScriptu su karakteristike sa svojim vanjskim okruženjem. Kao i u JavaScript-u, sve varijable imaju vezu za tip. JS koristi samo obvezujuće referencom - što odgovara C ++ 11, a vijek trajanja ne-lokalnih varijabli snimljenih funkcijama se distribuira na životu funkcije.

Krugovi u JavaScript obično se pojavljuju na jezicima s prvoklasnim vrijednostima. Takvi jezici omogućavaju vam prenošenje funkcija kao argumenata. A također se vratite iz funkcija poziva i pričvrstite na promjenjiva imena. To se događa kao jednostavne vrste, poput žica i cijelih brojeva.

U ovom primjeru, izraz Lambda (Lambda (\u003e \u003d (knjiga knjiga-prodaja) Prag) pojavljuje se u funkciji najprodavanijeg knjige. Kada se izračunava lambda izračuna, krug stvara zatvaranje koje se sastoji od koda za izražavanje lambda i reference na varijablu praga, što je besplatna varijabla unutar lambda izraza. Zatvaranje se zatim prenosi filtriranom funkcijom koja ga više puta uzrokuje da odredi koje knjige treba dodati na popis rezultata i koji bi trebali biti odbačeni.

Budući da postoji zatvaranje u vrijednosti praga, potonji ga može koristiti svaki put kada njeni filter uzrokuje. Sama funkcija filtra može se savršeno definirati odvojena datoteka. Evo istog primjera, prepisano u JS-u. To pokazuje kako se zatvara pod napajanjem na Javascript-u.

Ključna riječ se ovdje koristi umjesto globalne funkcije filtra, ali ostatak strukture i učinak koda su isti. Funkcija može stvoriti bliže i vratiti ga jer u ovom slučaju doživljava izvršenje funkcije s varijablama F i DX i DX i dalje funkcioniraju nakon derivata, čak i ako je izvršenje napuštalo njihov opseg i više nisu vidljive.

Na jezicima, bez zatvaranja, vijek trajanja automatske lokalne varijable poklapa se s izvršenjem okvira snopa, gdje se proglašava ova varijabla. Na jezicima sa JavaScript-om, zatvaračima i funkcijama života, varijable moraju nastaviti postojati dok ne postoje nikakve postojeće brave na njih. To se najčešće implementira pomoću neke oblike kolekcije smeća.

Prednost zatvaranja je da zadržava opseg, "lanac vidljivosti" vanjskog ili "roditeljskog" konteksta izvršenja. Takvo se ponašanje može koristiti na više načina i postao je koristan alat za sprečavanje raznih javascript grešaka. Jedan od najčešćih je problem "petlji".

Problem s ciklusom javlja se kada korisnik kreira funkciju u ciklusu i očekuje trenutnu vrijednost varijable u ovome nova funkcijaČak i ako se promijeni u kontekstu ciklusa prije poziva nove funkcije. Zatvaranje korištene na ovaj način više nemaju referentnu transparentnost i, stoga, više nisu čiste funkcije, međutim, obično se koriste u nečistim funkcionalnim jezicima kao što su shema. Da biste shvatili što se zatvara u JavaScript-u, morate razmotriti slučajeve upotrebe. U stvari, u praksi imaju mnogo aplikacija:

  1. Mogu se koristiti za određivanje upravljačkih struktura. Na primjer, sve standardne SmallTalk upravljačke strukture, uključujući grane (ako / zatim / inače) i ciklusi (dok i za), određuju se pomoću objekata čije metode preuzimaju. Korisnici također mogu lako koristiti zatvaranje za određivanje upravljačke strukture. Na jezicima koji implementiraju svrhu možete stvoriti multifunkcionalni medij, omogućujući vam da povjerljivo komunicirate i promijenite ovo okruženje. Zatvaranje se koristi za implementaciju objekata sistema.
  2. Izrada privatnih i javnih promjenjivih metoda pomoću predložaka modula. Zbog činjenice da su vraćene karakteristike naslijeđene područje roditeljske funkcije, dostupne su svim varijablama i argumentima u ovom kontekstu.
  3. Korisno je u situaciji u kojoj funkcija koristi isti resurs za svaki poziv, ali takođe stvara i sam resurs. Ova okolnost čini metodu neučinkovitim, koja se eliminira isključivo zatvaranjem.

Prema MDN-u (Mozilla Developer Network) "Zatvaranje su funkcije sa neovisnim varijablama koje" sećate "okruženju njegove kreacije". I, po pravilu, kada je funkcija završena, njegove lokalne varijable više ne postoje. Shvatite kako se zatvaranje u JavaScript radi, možete razmotriti nekoliko mehanizama. Prva je formalna logika. Na primjer, primjenjujući funkciju Logname, koja uzima jedno ime kao parametar i registrira ga. Zatim kreiram ciklus za prolaz kroz popis imena, postavite 1. Istek-aut, a zatim nazovite funkciju Logname kako prolazi u trenutnom imenu.

Na prvoklasnom jeziku možete manipulirati na isti način kao i druge vrste podataka, kao što su int ili string. Samo ovaj mehanizam omogućava mnogima da stvore nevjerojatne stvari, na primjer, dodjeljuje promjenjivu funkciju do naknadnog poziva ili ga prenose kao parametar druge funkcije.

Ovo princip koriste mnoge strukture, kao i HIDNERS DOM događaji. Prvo, događaj je "slušanje", a zatim dodijelite funkciju povratnog poziva koja se naziva svaki put kada se događaj pokrene.

Anonimna funkcija je funkcija bez imena. Praktično novačni programeri sastaju ih svakodnevno, bez razumijevanja igre s brojevima. Na primjer, obavljanje operacije dodavanja, možete proći kroz varijable, na primjer:

  • var X \u003d 3;
  • y \u003d 5;
  • vAR Z \u003d x + y.

Ili ako ne namjeravate ponovno obraditi brojeve: var z \u003d 3 + 5;

Ovo su anonimne sobe. Za anonimne funkcije možete ih proglasiti kada se koriste "u letu" - bez prolaska varijable. Na primjer, uzmite funkciju od prethodno:

(Upozorenje ("Ceci est une fonchan anonimy");

Štaviše, postoji alternativna sintaksa deklaracije funkcije koja naglašava da obje funkcije mogu biti anonimne i odnose se na jednostavne varijable, što je prikladan način za instaliranje funkcije povratnog poziva.

U stvari, to je isti mehanizam, ali sa ove tačke gledišta omogućit će vam da vidite kako je funkcija zatvorena iznutra. Kao što se vidi, jer su funkcije varijable, poput drugih, nema razloga za koji se ne mogu lokalno definirati. U nultom nalogu, kao što su C, C ++ i Java, sve se funkcije određuju na jednom nivou vidljivosti, u istoj klasi ili na globalnoj razini. S druge strane, u JavaScript-u, lokalna funkcija nestaje, kao i druge lokalne varijable, čim se rod funkcija završi, tako da nije vidljiva iz drugih funkcija.

To je zapravo teško, ali JavaScript ima način za praćenje vidljivosti varijabli, pa čak i na dva načina. Dodjeljivanje globalne varijable u JavaScript-u ima isti mehanizam kao i u Java - složenim predmetima, nizovima, dom elementima i drugima prenose se referencom, tako da u sljedećem kodu:

var tab \u003d; Var tab2 \u003d tab.

Gdje su tab i tab2 dvije veze na isti stol, tehnički su to pokazatelji koje kontroliraju sakupljač smeća. Funkcije se takođe prenose referencom. GlobalFN varijabla više nije skrivena. Narudžba vam omogućava da učinite ono što se pokazuje na primjeru zadatka zatvaranja JavaScripta.

Ovako možete izvući funkciju iz lokalnog konteksta, ako funkcija zadovoljava druge lokalne varijable. Jednostavan primjer: Automatsko povećanje, funkcija koja vraća cijeli broj koji se povećava za svaki put na pozivu. Konkretno, potrebe funkcija Inc, koje se ponašaju kako slijedi:

// retourne 0 inc ();

// retourne 1 inc ();

// retourne 2 inc ();

Uz zatvaranje izgleda:

funkcijska mačinka () (var x \u003d 0; povratna funkcija () (povratak x ++;)) var inc \u003d markenc ();

U posljednjem redu u trenutku kada se stvori varijabla Inc, nosi neke varijable koje su u ovom slučaju u ovom slučaju x. Stvara određeni nevidljivi objekt oko funkcije koja sadrži ovu varijablu. Ovaj objekt je funkcija zatvaranja JavaScript-a. Istovremeno, svaka kopija funkcije imat će svoje zatvaranje:

vAR Inc1 \u003d Makeinc ();

var Inc2 \u003d Makeinc ();

Kao što se vidi, zatvaranje je vrlo korisno u mnogim slučajevima.

Da bi se izbjegli sukob varijabli na ime, obično se koristi prostor imena. U JavaScript, imena imena su predmeti poput bilo kojeg drugog.

Naravno, A.X i B.X nisu iste varijable. Međutim, ako samo trebate pokrenuti skriptu, bez potrebe za uštedom varijabli za ostatak, anonimna funkcija se može koristiti kao zatvaranje. To daje blago čudnu sintaksu. Iako su dva kodeksa u sredini prilično uobičajene, s druge strane, funkcija koja se nalazi u blizini se izvodi "u letu". Obratite pažnju na zagrade () na kraju. I biti u mogućnosti napraviti zatvaranje, anonimna funkcija Sama mora biti okružena okruglim zagradama.

U ovoj anonimnoj funkciji koristite lokalnu varijablu, odlomak. Ovo je odličan način za sprečavanje sukoba ili neskladnosti imena, ali i protiv aTAKS XSS. Prilagođene varijable su zaštićene, niko ih ne može promijeniti kako bi utjecali na ponašanje skripte.

Postoji opcija: (funkcija () (// ...) ());

Istovremeno skrenite pažnju na permutaciju zagrade. Razlika između ove dvije mogućnosti je prilično teško objasniti jer su povezane s načinom na koji je kôd pročita leksički analizator. U oba slučaja, funkcija se smatra izraz, ali ovaj se izraz ne procjenjuje istovremeno. Samo se treba sjetiti da uzima dva para okruglih zagrada: jedan oko funkcije i jedan iza nje.

JavaScript programiranje u ciklusima

Kada korisnik obavlja velike količine JavaScript programiranja, teško je to izbjeći cikluse. Netko je lud, nakon čega dolaze u ideju da svaka primjena JavaScripta ima ozbiljnu grešku. Ako programer već ima ciklus za koji ne želi pretvoriti u upotrebu funkcije iteratora, sve što treba učiniti je zatvaranje u kojem definira nove varijable. Popravljaju trenutnu vrijednost varijabli i variraju na svakoj iteraciji. Trik za snimanje varijabli je da se vanjsko zatvaranje izvodi odmah tokom trenutne iteracije ciklusa. Možete koristiti jedan od ova dva približna pristupa.

Sada je još jedno pojednostavljeno rješenje ovog problema, jer je ključna riječ koja se pruža u Firefoxu i u Chromeu. TO JE ključna riječ Umesto var varijabilnog bloka. Neka radi magično, jer je proglašena nova varijabla J, vrijednost čije je vrijednost fiksirana zatvaračem unutar ciklusa. Međutim, mora se imati na umu da ne i dalje postoji nakon završetka iste iteracije ciklusa, jer je lokalno.

Petlja i funkcija

Za petlju u JavaScript se ne pojavljuje, kao i za ciklus u C ili Javi. U stvari, više liči na PHP. Najvažnije znanje o ciklusima u JS-u je da ne stvaraju područje djelovanja. JS nema blok sfere, samo funkcija zapremine. Ova nekretnina može se uzeti u obzir na sljedećem fragmentu:

funkcija foo () (var bar \u003d 1;

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

Jasno je da je bar dostupan u cijeloj funkciji. Prije nego što će prva iteracija ciklusa baza biti neodređena. Nakon ciklusa imat će vrijednost 41 (i ja ću biti 42). Dakle, svaka varijabla proglašena bilo gdje u funkciji bit će dostupna svuda u funkciji i bit će valjana samo nakon što je imenovana za njega.

Kapci i agregacija

Zatvaranje nije ništa drugo nego funkcije, unutar ostalih funkcija i prenose se u neki drugi kontekst. Nazivaju se zatvaranjem, jer se zatvaraju kroz lokalne varijable, odnosno dostupne drugim područjima sfere. Na primjer, vrijeme, x definirano kao parametar foo, a var bar \u003d foo (2) () će se vratiti 84.

Vraćena funkcija foo ima pristup x. Ovo je sve važno jer pomaže programerima da kreiraju funkcije unutar ciklusa, ovisno o ciklusu varijabli. Razmislite o ovom fragmentu, koji dodjeljuje procesor za razne elemente:

// Elementi su niz 3 DOM elementa var vrijednosti \u003d ["foo", "bar", "baz"];

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

elementi [i] .onclick \u003d funkcija () (upozorenje (podaci);

Vrijednost koju će upotrijebiti upozorenje kada će se pritisnuti, bit će isti za sve, naime Baz. Do tada se naziva rukovatelj događaja, za već završen. JS nema blok-prostor, I.E. Svi rukovodioci koriste vezu na istu varijablu podataka. Nakon petlje, ova vrijednost će biti vrijednosti. Svaka promjenjiva deklaracija stvara jedno mjesto u memoriji za pohranu. Za, ove se podaci ponovo mijenjaju i opet, položaj u memoriji ostaje nepromijenjen.

Svaki ručnik događaja ima pristup istom položaju u memoriji. Jedino rješenje je ući u drugo područje koje "ispravlja" trenutnu vrijednost podataka. JS ima samo niz funkcija. Stoga se unosi druga funkcija. Primjer:

funkcija CreateeVenthandler (x) (povratna funkcija () (upozorenje (x);

za (var i \u003d 0, l \u003d elementi.length;

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

elementi [i] .onclick \u003d createeventhandler (podaci);

Djeluje, jer će vrijednost podataka biti pohranjena u lokalnom području, CreateEVenthandler i ova funkcija se izvode na svakoj iteraciji. Može se napisati ukratko, koristeći izvršne funkcije odmah:

za (var i \u003d 0, l \u003d elementi.length;

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

elementi [i] .onclick \u003d (Funkcija (x) (funkcija () (upozorenje (x);

Praktični primjer zatvaranja u JavaScript-u

Ako se korisnik zatvara ravno iznad koda u pretraživaču, može naići na problem, jer može napraviti bilo koju sintaksu grešku. Ako izvršava kôd izravno u pretraživaču, šanse su vrlo visoke da ne sastavljaju postupak sastavljanja WebPacka. Moguća rješenja:

funkcijski rad (ime) (

povratna funkcija (tema) (

konzola.log (šta je $ (tema) u $ (naziv));

rad ("Javascript") ("zatvaranje");

Prvo, funkcija se naziva, a argument imena se prenosi. Sada ova funkcija vokabulara također vraća funkciju koja prihvaća i argument teme. Ova značajka registrira izlaz, a izlaz ima pristup varijabli.

Područje insajderskih funkcija nije ograničeno na ovu funkciju, tako da se koncept naziva zatvaranje, jer ima pristup ovom području vanjski parametar. Povratna funkcija ima pristup vanjskom leksičkom području ili kontekstu. Kada programer nazove funkciju koja ga takođe vraća, a zatim se prvo naziva promenljiva funkcija Uvek dostupan za unutrašnju funkciju. Daljnji primjer sa sljedećim kodom.

Primjer interne funkcije

Pročitajte više o zatvaranju u JavaScript-u može se reći na drugom primjeru. Sada je ovo okruženje ispunjenja uništeno, ali ime parametra i dalje postoji. Stvoren je novi interni funkcionalni medij, što je anonimna funkcija. Ima pristup prostoru vanjskog leksičkog okruženja.

Dakle, u varijabilnoj vanjskom okruženju, i dalje postoji na takav način da anonimna funkcija ima pristup varijabilnim otiscima imena u konzoli, na primjer, "Što je zatvaranje u JavaScript". Interna anonimna funkcija //main.js

fabrika funkcije () (var proizvodi \u003d;

i ++) (proizvodi.push (funkcija () (konzola.log (i);

) Povratni proizvodi;

) Var sapun \u003d tvornički ();

Rezultat ovog primjera je prilično beznačajan i jednak 2.

Kada je sapun () nazvan vanjska varijabla konteksta, uvijek 2, jer je u ciklusu stanje u i<2, поэтому при этом значение i равно 2, а во время вызова нужно напечатать значение в консоль так, она всегда пишет 2. То же самое для мыла - soap ().

Stvaranje funkcija "u letu"

Možete stvoriti fabriku funkcija - Funkcionalnost koja vrši prilagođene zadatke. Rezultirajuća funkcija iz tvornice funkcija bit će zatvaranje, medij za kreiranje skladištenja.

var funkcionalni naftino \u003d funkcija (broj1) (povratni broj1 * num2;

Gore vam omogućuje prenos jednog funkcionalnog broja. Tada funkcionalnost vraća zatvaranje, pamćenje broja1 vrijednosti. Rezultirajuća funkcija množi se originalni broj1 puta vrijednost broja2, koja se prenosi kada se naziva.

var mult5 \u003d funkcionalnost (5);

var multi10 \u003d Funkcionalna (10);

Gore navedeno stvara funkcije Mult5 i Mult10. Sada se možete odnositi na bilo koju od ovih funkcija prenošenjem novog broja za množenje do 5 ili 10. Sada možete vidjeti rezultat.

Zatvaranje je jedno od najmoćnijih JavaScript funkcija, ali ne može se ispravno koristiti bez razumijevanja suštine. Oni su relativno jednostavni za stvaranje slučajno, to je koliko opasno zatvaranje JavaScript. Njihovo stvaranje ima potencijalno štetne efekte, posebno u nekim relativno uobičajenim okruženjima web pretraživača. Kako bi se izbjegli slučajni sudar s nedostacima i iskoristite prednosti koje nude, potrebno je razumjeti njihov mehanizam.

U ovom ću članku pokušati objasniti područja vidljivosti i zatvaranja u JavaScript-u, u kojoj mnogi doživljavaju poteškoće.

Uvođenje

Postoji dosta članaka u mreži koja pokušava objasniti područja vidljivosti i zatvaranja, ali generalno bih rekao da većina njih nije u potpunosti razumljiva. Pored toga, u nekim se člancima pretpostavlja da ste programirani prije 15 drugih jezika, iako mislim da većina ljudi piše na JavaScriptu ima samo iskustvo u HTML-u i CSS-u, a ne u C ili Javi.

Slijedom toga, svrha ovog članka je objasniti za sve - koji je opseg vidljivosti i zatvaranje, kao što rade, i što je najvažnije što njihova prednost. Prije čitanja ovog članka, morate znati osnovne pojmove o varijablama i funkcijama u JavaScript-u.

Područje vidljivosti

Opseg znači gde su dostupne varijable i funkcije i u kom se kontekstu izvrše. Varijabla ili funkcija mogu se definirati u globalnom ili lokalnom području vidljivosti. Varijable imaju takozvana funkcija vidljivosti funkcije, a funkcije imaju isti opseg kao varijable.

Globalni opseg

Kada je nešto globalno, znači da je dostupan s bilo kojeg mjesta u vašem kodu. Razmotrite primjer:

var majmun \u003d "gorila"; Funkcija Greetvisitor () (Povratna upozorenja ("Pozdrav dragi čitač blogova!"););)

Ako je ovaj kôd izveden u web pretraživaču, tada bi područje vidljivosti bilo prozor, teover će biti dostupan za sve što se izvrši u prozoru.

Lokalni opseg

Za razliku od globalnog područja vidljivosti, lokalni prostor vidljivosti je kada se nešto definira i dostupan samo u nekoj dijelu koda, kao što je funkcija. Razmotrite primjer:

funkcija TalkDirty () (var izreka \u003d "Oh, ti mali VB ljubavnik, ti"; povratni upozorenje (rekavši);) upozorenjem (rekavši); // bacala je grešku

U ovom primjeru, varijabla izreke dostupna je samo unutar funkcije TalkDirty, napolju koja nije definirana. Napomena: Ako ste identificirani rekavši bez ključne riječi var, automatski bi postao globalni.

Pored toga, ako ste bi ugniježđene funkcije, interna funkcija imala će pristup funkcijama u kojima je ugrađena, kao i varijable:

funkcijski kapital (povratak FirstName.Tuppercase ();) var kapitalizirani \u003d kapitalni ime (); povratni kapitalizirani;) upozorenje (savename ("Robert"); // vraća "Robert"

Kao što ste upravo vidjeli, naziv kapitala interne funkcije ne treba prenositi nikakve parametre, jer Ima puni pristup parametru prvog imena u vanjskoj funkciji savename. Za veću jasnoću razmotrite još jedan primjer:

funkcija braće i sestre () (var braća \u003d ["JOHN", "PETER"]; Funkcija braće se () (Var SiblingsLength \u003d BOLVERS.Length; Returns BOLBINGSLENGH;) () () + "Braća i sestre: nn" + braće.join ("n");) povratak joinsiblingrnames ();) upozorenje (braća i sestre ()); // izlazi "Imam 3 braće i sestre: John Liza Peter"

Kao što vidite, obje interne funkcije imaju pristup matriji braće i sestrama, a svaka interna funkcija ima pristup drugoj unutrašnjoj funkciji iste razine (u ovom slučaju Joinsiblingrnames ima pristup brašnoj osobi). Međutim, varijabla braće i seblings unutar braće se nalazi samo u ovoj funkciji, i.e. U ovom području vidljivosti.

Krug

Sada kada imate jasniju predstavu o područjima vidljivosti, dodat ćemo ih zatvaranje. Krugovi su izrazi, obično funkcije koje mogu raditi sa setom varijabli u određenom kontekstu. Ili, jednostavnije riječi, interne funkcije koje se odnose na lokalne varijable vanjskih funkcija zatvaraju se. Na primjer:

funkcija dodajte (x) (povratak x + y;);) var add5 \u003d dodajte (5); var no8 \u003d add5 (3); Upozorenje (No8); // vraća 8.

Blimey! Šta se događa ovde? Radimo se:

1. Kada nazovemo funkciju Dodaj, vraća funkciju.

2. Ova funkcija zatvara kontekst i pamti kako je parametar x u ovom trenutku (I.E. u ovom slučaju vrijednost 5)

3. Kada je rezultat dodavanja funkcije dodijeljen dodatnom varijabli, uvijek će znati što je x bilo prilikom stvaranja ove varijable.

4. Varijabilni dodatak, odnosi se na funkciju koja će uvijek dodati vrijednost 5 na bilo koji argument koji se prenosi na njega.

5. To znači da kada nazovemo Add5 s vrijednošću 3, on će preklopiti broj 5 i 3, a vratiti se 8.

U stvari, u svetu JavaScript funkcija Add5 izgleda ovako:

funkcija dodajte5 (y) (povratak 5 + y;)

Zloglasni problem ciklusa
Koliko puta ste stvorili cikluse u kojima ste željeli dodijeliti vrijednost I na bilo koji način, na primjer, elementu i shvatio da je samo zadnja vrijednost koju sam vraćen?

PogrešnoŽalba

Pogledajmo ovaj pogrešni kod koji stvara 5 elemenata , dodaje vrijednost I kao tekst u svakom elementu i onlick, koji se očekuje da će proizvoditi upozorenje s vrijednošću I za ovu referencu, I.E. Ista vrijednost kao i u tekstu elementa. Tada se elementi dodaju u tijelo dokumenata:

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

Svaki element sadrži ispravan tekst, i.e. "Link 0", "Link 1", itd. Ali bez obzira na vezu koju ste kliknuli, prikazuje upozorenje s brojem 5. Što je? Razlog je taj što vrijednost varijable povećava za 1 sa svakom iteracijom ciklusa, i zato što Onclick događaj se ne izvršava, ali jednostavno se odnosi na element Vrijednost se povećava.

Stoga ciklus i dalje radi dok ne postanem jednak 5, što je posljednja vrijednost prije izlaska iz funkcije dodatka dodataka. Nadalje, svaki put kada se aktivira događaj Onclick, zadnja vrijednost koju sam uzeo.

Pravilna privlačnost

Ono što trebate učiniti je stvoriti zatvarač. Kao rezultat, kada primijenite vrijednost I na događaj događaja Onclick , Bit ću dodijeljen vrijednost ja u to vrijeme. Na primjer, ovako:

funkcija dodajte () (za (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;

Pomoću ovog koda, ako kliknete na prvi element, upozorenje će izdati "0", na drugom - "1" itd. Rješenje je da interna funkcija primijenjena na onclick događaju stvara zatvaranje u kojem se brojčani parametar num, I.E. na vrijednost ja u to vrijeme.

Ova se značajka "sjeća" željene vrijednosti, a zatim može vratiti odgovarajuću cifru kada se aktivira događaj Onclick.

Funkcije sigurnih izvršenja

Funkcije za sigurnu izvršavanje su takve funkcije koje počinju odmah izlagati i stvoriti svoje zatvaranje. Razmotrite primjer:

(Funkcija () (VAR DOG \u003d "Njemački ovčar"; upozorenje (pas);))) (); Upozorenje (pas); // vraća nedefinirano.

Dakle, varijabla pasa dostupna je samo u ovom kontekstu. Razmislite, skriveni varijabilni pas ... ali, moji prijatelji, najzanimljivija stvar započinje s tim! Riješio je naš problem ciklusom, a ujedno je i za Yahoo JavaScript modul uzorak.

Yahoo JavaScript modul uzorak

Suština ovog obrasca je da koristi sigurnu izvršnu funkciju za stvaranje zatvaranja, pa omogućava korištenje privatnih i javnih svojstava i metoda. Jednostavan primjer:

var osoba \u003d funkcija () (// privatna var ime \u003d "robert"; povratak (getname: funkcija () (povratna imena), naziv: funkcija (ime \u003d novoime;););););); upozorenje (osoba.Name); // nedefinirano upozorenje (osoba.GetName ()); // "robert" osoba.setname ("Robert Nyman"); upozorenje (osoba.getName ()); // "Robert Nyman"

Prednost ovog pristupa je da se možete definirati, koji će biti otvoren u vašem objektu (i može se promijeniti) i to se zatvorilo, koje niko ne može kontaktirati ili mijenjati. Varijabla imena skrivena je izvan konteksta funkcije, ali je dostupna funkcijama GetName i Name imena, jer Oni stvaraju zatvaranje u kojima postoji veza do varijable imena.

Zaključak

Iskreno se nadam da su nakon čitanja ovog članka pridošlici i iskusni programeri dobili jasnu predstavu o tome kako u javascript-ovim radnim područjima vidljivosti i zatvaranja. Pitanja i recenzije su dobrodošli, a ako imate nešto važno da nešto dopustite, onda mogu ažurirati članak.

Dobro kodiranje!

Krugovi u JavaScript-u. Koristi se za skrivanje promjenjivih vrijednosti i trgovina funkcija. Poanta je da se kada je zatvorena, jedna funkcija kreira u kojoj su navedene varijable i što kao rezultat njenog rada vraća svoju ugrijanu funkciju. Zatim, u njemu (u glavnoj funkciji) stvorena je ugrađena funkcija u kojoj se naprave neke operacije sa varijabli varijabilne funkcije i koje vraća rezultat ovih operacija. Zatim je glavna funkcija jednaka varijabli - ova varijabla može se pozvati koliko vremena i međutim, vrijednosti varijabli glavne funkcije bit će pohranjene i ažurirane. Ona je "zatvorena".

Kao što znate, u JavaScript-u polje vidljivosti lokalnih varijabli (Deklarirana var riječ) To je tijelo funkcije unutar koje su definirane.

Ako proglasite funkciju unutar druge funkcije, prvi dobija pristup varijablama i argumentima potonjeg:

Šifra: Funkcija Outerfn (MYARG) (
Var Myvar;
Funkcija Innerfn () () (
// ima pristup Myvaru i Myargu
}
}

Istovremeno, takve varijable i dalje postoje i ostaju dostupne interne funkcije čak i nakon vanjske funkcije u kojoj su definirani izvršeni.

Razmotrite primjer - funkcija koja vraća broj EigenValues:

Šifra: Funkcija CreateCounter () (
var numerifcalls \u003d 0;
Povratna funkcija () (
Povratak ++ numberofcalls;
}
}
var fn \u003d CreateCounter ();
fn (); // jedan
fn (); // 2.
fn (); // 3.

U ovom primjeru funkcija koju vraća CreateCounter koristi brojčane varijable brojčane vrijednosti, što štedi željenu vrijednost između njegovih poziva (umjesto da odmah zaustavi svoje postojanje sa povratom CreateCounter-a).

Ovim je svojstvima koje su takve "ugniježđene" funkcije u JavaScript-u nazivaju zatvaranje (termin koji je došao iz programskih funkcionalnih jezika) - oni "bliže" varijable i argumente funkcije unutar kojih su definirani.

Primjena zatvaranja

Pojednostavljuje malo gornjeg primjera - uklonite potrebu za odvojeno nazvati funkcije CreateCounter, što ga čini aneksičkim i pozivom odmah nakon svog oglasa:

Kod: var fn \u003d (Funkcija () () (
var numerifcalls \u003d 0;
Povratna funkcija () (
Povratak ++ numberofcalls;
}
})();

Takav dizajn omogućio nam je da obvezujemo podatke u funkciju, što je ostalo između njegovih izazova jedna je od koristi zatvaranja. Drugim riječima, uz pomoć njih možemo stvoriti funkcije koje imaju varijabilnu državu.

Druga dobra upotreba zatvaranja - stvaranje funkcija, zauzvrat, također stvarajući funkcije - ono što bi se neki zvali T.N. MetaPrograming.
Na primjer:

Šifra: var createehellofoncks \u003d funkcija (ime) (
Povratna funkcija () (
Upozorenje ("Zdravo" + naziv);
}
}
var SayHelloHabrahabr \u003d CreateEhelloofunkciju ("hamrahabr");
sayhellohabrahabr (); // Upozorenja "Zdravo, Habrahabr"

Zahvaljujući zatvaranju, vraćena funkcija "seća se" parametri prenose na funkcije stvaranja koje nam trebaju za takve stvari.

Slična se situacija pojavljuje kada ne vratimo internu funkciju i družimo se na bilo kojem događaju - budući da se događaj nastaje nakon što je funkcija ispunjena, zatvaranje pomaže ne izgubiti podatke koji se prenose.

Razmotrite malo složeniji primjer - metoda koja povezuje funkciju na određeni kontekst (I.E., predmet kojem će ta riječ navesti u njoj).

Šifra: funkcija.prototip.bind \u003d Funkcija (kontekst) (
var fn \u003d ovo;
Povratna funkcija () (
Vratiti fn.apply (kontekst, argumenti);
};
}
var HelloPage \u003d (
Ime: "HABRAHABR",
Init: Funkcija () (
Upozorenje ("Zdravo" + this.name);
}
}
//Window.onoad \u003d HellopAge.init; // alertnul bi nedefinisao, jer To ukazuje na prozor
window.olload \u003d hellopAge.init.bind (HellopAge); // sada sve radi

U ovom primjeru, uz pomoć zatvaranja, funkcija, aktivira se vezanje "Ohm, pamti početnu funkciju i kontekst dodijeljen njoj.

Sljedeće, u osnovi drugačija upotreba zatvaranja - zaštita podataka (enkapsulacija). Razmotrite sljedeći dizajn:

Kod: (Funkcija () (

})();

Očito, unutar zatvaranja imamo pristup svim vanjskim podacima, ali ima svoje. Zbog toga možemo okružiti dio koda poput dizajna kako bismo zatvorili lokalne varijable od pristupa vani. (Jedan primjer njegove upotrebe može se vidjeti u izvornom kodu jQuery biblioteke, koji okružuje zatvaranje sav njegov kod tako da ne izlaže varijable samo na njega).

Međutim, postoji jedna povezana s takvom zamkom upotrebe - značenje riječi koje se gubi unutar zatvaranja. Riješeno je na sljedeći način:

Kod: (Funkcija () (
// najviše ovo će se nastaviti
)). Pozovite (ovo);

Razmotrite još jedan prijem iz iste serije. Njegovi programeri Yahoo UI udredjeli su ga navodeći, nazivajući ga "uzorkom modula" i pisao čitav članak na njemu u službenom blogu.
Dopustite da imamo predmet (singleton) koji sadrži bilo kakve metode i svojstva:

Šifra: var mymodule \u003d (
Ime: "HABRAHABR",
SayPreved: Funkcija (ime) (
Upozorenje ("prevedeno" + naziv.touppercase ())
},
ovo.Saypreved (this.name);
}
}
Mymodule.sayprevedtohabrahabr ();

Uz pomoć zatvaranja možemo izrađivati \u200b\u200bmetode i svojstva koja se ne koriste izvan objekta, privatni (i.e. Dostupan samo mu):

Šifra: var mymodule \u003d (funkcija () (
Var ime \u003d "hamrahabr";
Funkcija SayPreved () (
Upozorenje ("prevedeno" + naziv.touppercase ());
}
Povratak (
SayprevedtoHabrahabr: Funkcija () (
Saypreved (ime);
}
}
})();
Mymodule.sayprevedtohabrahabr (); // upozorenja "preveli su Habarhabr"

Konačno, želim opisati zajedničku grešku što mnogo voze u stupa u slučaju neznanja o tome kako zatvara rad.

Dopustite nam da imamo niz veza, a naš zadatak je da se učinimo tako da kada kliknete na svaki alert, prikazuje se njegov niz sekvencija.
Prva odluka koja pada na pamet izgleda ovako:

Kod: za (var i \u003d 0; i< links.length; i++) {
Upozorenje (i);
}
}

U stvari, ispada da se kada kliknete na bilo koju vezu, prikazuje se isti broj - vrijednost Links.Length. Zašto se to događa? U vezi s zatvaranjem, najavljena pomoćna varijabla nastavljam da postoji, sa i u trenutku kada kliknemo na vezu. Od tada je ciklus već prošao, ostaje jednak broju veza - ta vrijednost koju smo vidljivi prilikom klikanja.

Ovaj problem se rješava na sljedeći način:

Kod: za (var i \u003d 0; i< links.length; i++) {
(Funkcija (I) (
Linkovi [i] .onclick \u003d Funkcija () (
Upozorenje (i);
}
))) (I);
}

Evo, uz pomoć drugog zatvaranja, "Shadow" varijablu I, stvarajući to kopiju u svom lokalnom području vidljivosti na svakom koraku ciklusa. Zahvaljujući tome, sve radi sada kao zamišljeno.

To je sve. Ovaj članak, naravno, ne tvrdi da je iscrpan, ali netko se nadam, još uvijek pomaže da to shvatim.

zY.
Za spremanje između poziva lakše korištenje func_name.attr Tip:

Kod: Funkcija (resetiranje) (resetiranje) (
ako (resetiraj ||! countit.cnt) countit.cnt \u003d 0;
povratni župnik.cnt ++;