Kaip veikia „JavaScript“ blokatoriai?

Kaip paaiškintumėte, kodėl „JavaScript“ uždarymas tiems, kurie žino apie jų sąvokas (pvz., Funkcijas, kintamuosius ir pan.), Bet nesupranta uždarymo?

Mačiau Wikipedijoje pateiktos schemos pavyzdį , tačiau, deja, tai nepadėjo.

7654
21 сент. rugsėjo 21 d. 2008-09-21 17:12 '08 at 17:12 pm 2008-09-21 17:12
@ 89 atsakymai
  • 1
  • 2
  • 3

„JavaScript“ uždarymas pradedantiesiems

Pateikė Morris antradienį, 2006-02-21 10:19. Nuo to laiko bendruomenė buvo redaguota.

Uždarymas nėra magija

Šiame puslapyje paaiškinamas uždarymas, kad programuotojas galėtų juos suprasti - naudojant „JavaScript“ kodą. Tai nėra guru ar funkcinių programuotojų.

Uždarymą sunku suprasti, kai tik siuvama pagrindinė koncepcija. Tačiau jie negali būti suprantami skaitant teorinius ar akademinius paaiškinimus!

Šis straipsnis skirtas programuotojams, turintiems pagrindinę programavimo patirtį ir gali skaityti šią „JavaScript“ funkciją:

aukščiausios klasės funkcijas ;  yra išraiška, kuri gali būti susijusi su kintamaisiais, esančiais jos taikymo srityje (kai ji buvo anksčiau paskelbta), priskiriama kintamajam, perduodama kaip funkcijų argumentas, arba grąžinami kaip funkcijos rezultatas. 

Uždarymo pavyzdys

Šis kodas grąžina funkcijų nuorodą:

 function say667() { // Local variable that ends up within closure var num = 42; var say = function() { console.log(num); } num++; return say; } var sayNumber = say667(); sayNumber(); // logs 43 

4 pavyzdys

Visos trys pasaulinės funkcijos turi bendrą nuorodą į tą patį uždarymą, nes visos jos yra deklaruojamos per vieną skambutį į setupSomeGlobals() .

 function sayAlice() { var say = function() { console.log(alice); } // Local variable that ends up within closure var alice = 'Hello Alice'; return say; } sayAlice()();// logs "Hello Alice" 

Tricky: taip pat atkreipkite dėmesį, say , say kintamasis taip pat yra uždarymo viduje, ir jis gali sayAlice() bet kokią kitą funkciją, kuri gali būti deklaruota „ sayAlice() , arba ją galima gauti rekursiškai vidinėje funkcijoje.

6 pavyzdys

Tai tikra magija visiems žmonėms, todėl jūs turite tai suprasti. Būkite labai atsargūs, jei apibrėžiate funkciją per kilpą: vietiniai kintamieji nuo uždarymo gali neveikti taip, kaip galėtumėte pagalvoti.

Kad suprastumėte šį pavyzdį, turite suprasti „JavaScript“ „kintamo kėlimo“ funkciją.

 function newClosure(someNum, someRef) { // Local variables that end up within closure var num = someNum; var anArray = [1,2,3]; var ref = someRef; return function(x) { num += x; anArray.push(num); console.log('num: ' + num + '; anArray: ' + anArray.toString() + '; ref.someVar: ' + ref.someVar + ';'); } } obj = {someVar: 4}; fn1 = newClosure(4, obj); fn2 = newClosure(5, obj); fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4; fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4; obj.someVar++; fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5; fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5; 

Santrauka

Jei viskas atrodo neaiški, geriausia žaisti su pavyzdžiais. Paaiškinimų aiškinimas yra daug sudėtingesnis nei pavyzdžių supratimas. Mano paaiškinimai dėl uždarymo ir stekų rėmų ir kt. Jie nėra techniškai teisingi - tai neapibrėžti supaprastinimai, skirti padėti suprasti. Išsprendus pagrindinę idėją, vėliau galėsite gauti išsamią informaciją.

Pabaigos taškai:

  • Kai naudojate function kitoje funkcijoje, naudojamas uždarymas.
  • Kai naudojate eval() funkciją, naudojama uždarymo funkcija. Tekstas, kurį galite įvertinti, gali būti susijęs su vietiniais funkcijos kintamaisiais, o eval galite sukurti naujus vietinius kintamuosius su eval('var foo = …')
  • Kai naudojate new Function(…) ( funkcijos konstruktorius ) funkcijos viduje, jis nesukuria uždarymo. (Nauja funkcija negali remtis vietinio išorės funkcijos kintamaisiais.)
  • „JavaScript“ uždarymas yra tarsi išlaikyti visų vietinių kintamųjų kopijas, kaip ir išeinant iš funkcijos.
  • Tikriausiai geriausia manyti, kad uždarymas visada yra sukurtas kaip funkcijų įrašas, o prie šio uždarymo pridedami vietiniai kintamieji.
  • Naujas vietinių kintamųjų rinkinys išsaugomas kiekvieną kartą, kai funkcija vadinama uždarymu (atsižvelgiant į tai, kad joje yra funkcijų deklaracija, arba nuoroda į šią vidinę funkciją yra arba grąžinama, arba išorinė nuoroda yra tam saugoma).
  • Dvi funkcijos gali atrodyti taip, tarsi jos turi tą patį šaltinio kodą, bet turi visiškai kitokį elgesį dėl jų paslėpto uždarymo. Nemanau, kad „JavaScript“ kodas tikrai gali sužinoti, ar funkcija turi nuorodą, ar ne.
  • Jei bandote atlikti bet kokius dinaminio šaltinio kodo pakeitimus (pvz .: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola')); ) jis neveiks, jei myFunction funkcija yra uždarymas (žinoma, jūs jūs nemanote pakeisti šaltinių linijų vykdymo metu, bet ...).
  • Funkcijų deklaracijas galite gauti funkcijų deklaracijų viduje mdash funkcijų viduje, o uždarymą galite gauti daugiau nei viename lygmenyje.
  • Manau, kad paprastai uždarymas yra terminas ir funkcijai, ir užfiksuotiems kintamiesiems. Atkreipkite dėmesį, kad šiame straipsnyje nenaudojau šio apibrėžimo!
  • Įtariu, kad „JavaScript“ uždarymas skiriasi nuo tų, kurie paprastai randami funkcinėse kalbose.

jungtys

dėka

Jei ką tik sužinojote apie uždarymą (čia arba kažkur kitur!), Tada domiuosi bet kokiu jūsų atsiliepimu apie bet kokius pakeitimus, kuriuos galėtumėte pasiūlyti, kad šis straipsnis būtų aiškesnis. Siųsti žinutę morrisjohns.com (morris_closure @). Atkreipkite dėmesį, kad nesu „JavaScript“ guru - ne arti.


Originalų „Morris“ įrašą galima rasti interneto archyve .

6329
21 сент. atsakymą pateikė Joel Anair 21 sep . 2008-09-21 17:18 '08, 17:18, 2008-09-21 17:18

Kai matote funkcijų raktinį žodį kitoje funkcijoje, vidinė funkcija turi prieigą prie išorės funkcijos kintamųjų.

 function foo(x) { var tmp = 3; return function (y) { console.log(x + y + (++tmp)); // will also log 16 } } var bar = foo(2); // bar is now a closure. bar(10); 

Pirmiau minėta funkcija taip pat rašys 16, nes bar vis dar gali būti susijusi su x ir tmp , net jei ji nebėra regione.

Vis dėlto, kadangi tmp vis dar pakabina vidinės bar uždarymą, jis taip pat padidėja. Jis padidės kiekvieną kartą, kai skambinsite bar .

Paprasčiausias uždarymo pavyzdys yra:

border=0

 function foo(x) { var tmp = 3; return function (y) { console.log(x + y + tmp); x.memb = x.memb ? x.memb + 1 : 1; console.log(x.memb); } } var age = new Number(2); var bar = foo(age); // bar is now a closure referencing age. bar(10); 

Kaip tikėtasi, kiekvienas skambutis į bar(10) padidins x.memb . Negalite tikėtis, kad x paprasčiausiai nurodys tą patį objektą kaip age kintamasis! Po poros skambučių į bar age.memb bus 2! Ši nuoroda naudojama kaip atminties nutekėjimo su HTML objektais pagrindas.

3825
21 сент. atsakymas į „ Ali“ rugsėjo 21 d. 2008-09-21 18:16 '08 18:16 pm 2008-09-21 18:16

PRATARMĖ: šis atsakymas buvo parašytas, kai klausimas buvo:

Kaip ir senas Albertas, jis pasakė: „Jei negalite to paaiškinti šešerių metų vaikui, jūs to nesuprantate“. Na, aš bandžiau paaiškinti JS uždarymą 27 metų draugui ir visiškai nepavyko.

Ar kas nors gali manyti, kad aš esu 6 metai ir keistai domisi šiuo klausimu?

Aš esu tikras, kad buvau vienas iš tų žmonių, kurie bandė pažodžiui paimti pradinį klausimą. Nuo to laiko šis klausimas buvo keletą kartų mutuotas, todėl mano atsakymas dabar gali atrodyti neįtikėtinai kvailas ir netinkamas. Tikiuosi, kad bendroji šios istorijos idėja išlieka įdomi.


Aš esu didelis analogų ir metaforų gerbėjas, aiškindamas sudėtingas koncepcijas, todėl leiskite išbandyti savo ranką istorijoje.

Kartą:

Buvo princesė ...

 function princess() { 

Ji gyveno nuostabiame pasaulyje, pilname nuotykių. Ji susitiko su savo princu Charlesu, jodinėjo aplink pasaulį vienaragiu, kovojo su drakonais, susitiko su kalbančiais gyvūnais ir daugybe kitų fantastinių dalykų.

  var adventures = []; function princeCharming() {  } var unicorn = {  }, dragons = [  ], squirrel = "Hello!";  

Bet ji visada turėjo grįžti į savo nuobodu pasaulį ir suaugusiuosius.

  return { 

Ir ji dažnai papasakojo jiems apie savo paskutinį nuostabų nuotykį kaip princesę.

  story: function() { return adventures[adventures.length - 1]; } }; } 

Bet viskas, ką jie matė, buvo maža mergaitė ...

 var littleGirl = princess(); 

... pasakoja apie magiją ir fantaziją.

 littleGirl.story(); 

Ir nors suaugusieji žinojo apie tikrąsias princeses, jie niekada netikėtų vienaragiais ar drakonais, nes jie niekada jų nematė. Suaugusieji sakė, kad jie egzistuoja tik mažosios mergaitės vaizduotėje.

Bet mes žinome tikrąją tiesą; kad maža mergaitė su princesė viduje ...

... iš tikrųjų princesė su maža mergaitė.

2273
24 июня '11 в 21:49 2011-06-24 21:49 atsakymą pateikė Jacobas Swartwoodas birželio 24 d. 11 d. 21:49. 2011-06-24 21:49

Atsižvelgiant į šį klausimą rimtai, turime išsiaiškinti, kad tipiškas šešerių metų žmogus yra pažintinis sugebėjimas, nors, žinoma, tie, kurie domina „JavaScript“, nėra taip būdingi.

Dėl vaikystės raidos: nuo 5 iki 7 metų sakoma:

Jūsų vaikas galės sekti dviem etapais. Pavyzdžiui, jei sakote savo vaikui: „Eikite į virtuvę ir paimkite šiukšlių maišą“, jie gali prisiminti šią kryptį.

Šį pavyzdį galime naudoti, kad paaiškintume uždarymus taip:

Virtuvė yra arti, kurioje yra vietinis kintamasis, vadinamas trashBags . Viduje virtuvė yra funkcija getTrashBag funkcija, kuri gauna vieną šiukšlių maišą ir grąžina jį.

Tai galima koduoti javascript'e taip:

696
02 сент. atsakymas yra dlaliberte 02 sep . 2011-09-02 18:23 '11 at 18:23 2011-09-02 18:23

Šiaudų vyras

Turiu žinoti, kiek kartų mygtukas yra paspaudžiamas ir ką nors daryti kas trečią paspaudimą ...

Gana akivaizdus sprendimas

 <button id="button">Click Me!</button> 

Tai dabar veiks, bet ji įsiveržia į išorinę sritį, pridėdama kintamąjį, kurio vienintelis tikslas yra sekti paskyrą. В некоторых ситуациях это было бы предпочтительнее, так как вашему внешнему приложению может потребоваться доступ к этой информации. Но в этом случае мы меняем только каждый третий клик, поэтому рекомендуется включать эту функциональность внутри обработчика событий .

Рассмотрим этот вариант

 <button id="button">Click Me!</button>