Înțelegerea completă a închiderilor poate părea un drept de trecere la devenirea unui dezvoltator JavaScript.

Există un motiv pentru care poate fi dificil să înțelegeți închiderile – deoarece acestea sunt adesea predate înapoi. Este posibil să fi fost învățat ce înseamnă închiderea, dar este posibil să nu înțelegeți cum sunt utile dezvoltatorului mediu sau în propriul cod.

Deci, de ce contează închiderile în codul nostru JavaScript de zi cu zi?

În loc să vedem închiderile ca subiect de memorat pentru un fel de test pop, haideți să vedem ce serie de pași ne pot conduce la o închidere. După ce vom vedea care sunt acestea, vom descoperi de ce merită închiderile pentru a le cunoaște și a profita din codul dvs. JavaScript.

Doriți să urmăriți această lecție? Acest tutorial face parte din 2020 JS Bootcamp, un curs de peste 4 ore care vă arată cum să fiți un expert JavaScript prin tone de lecții practice, fără prostii. Obțineți acces instantaneu la JS Bootcamp aici.

Vedeți o închidere în sălbăticie?

Să presupunem că realizăm o clonă de aplicație a site-ului de blog Medium, și dorim ca fiecare utilizator să poată să aprecieze postări diferite.

Ori de câte ori un utilizator face clic pe butonul Apreciază, valoarea acestuia va fi mărită cu câte unul de fiecare dată.

Gândiți-vă la asta ca la butonul Medium clap:

https://www.routech.ro/wp-content/uploads/De-ce-ar-trebui-sa-stiti-inchiderile-JavaScript.gif

Se apelează funcția care se va ocupa de creșterea numărului cu 1 de fiecare dată handleLikePost și urmărim numărul de aprecieri cu o variabilă numită likeCount:

// global scope
let likeCount = 0;

function handleLikePost() {
  // function scope
  likeCount = likeCount + 1;
}

handleLikePost();
console.log("like count:", likeCount); // like count: 1

Ori de câte ori unui utilizator îi place o postare, apelăm handleLikePost și ne crește likeCount de 1.

Și acest lucru funcționează pentru că știm că funcțiile pot accesa variabile în afara lor.

Cu alte cuvinte, funcțiile pot accesa orice variabilă definită în orice domeniu părinte.

Cu toate acestea, există o problemă cu acest cod. De cand likeCount este în domeniul global și nu în orice funcție, likeCount este o variabilă globală. Variabilele globale pot fi utilizate (și modificate) de orice alt bit de cod sau funcție din aplicația noastră.

De exemplu, ce se întâmplă dacă, după funcția noastră, ne setăm greșit likeCount la 0?

let likeCount = 0;

function handleLikePost() {
  likeCount = likeCount + 1;
}

handleLikePost();
likeCount = 0;
console.log("like count:", likeCount); // like count: 0

Natural, likeCount nu poate fi niciodată incrementat de la 0.

Când o singură funcție are nevoie de o anumită bucată de date, trebuie doar să existe local, adică în cadrul acelei funcții.

Acum să aducem likeCount în funcția noastră:

function handleLikePost() {
  // likeCount moved from global scope to function scope
  let likeCount = 0;
  likeCount = likeCount + 1;
}

Rețineți că există o modalitate mai scurtă de a scrie linia în care creștem likeCount. În loc să spui likeCount este egală cu valoarea anterioară a likeCount și adăugăm unul ca acesta, putem folosi operatorul + = astfel:

function handleLikePost() {
  let likeCount = 0;
  likeCount += 1;
}

Și pentru ca acesta să funcționeze ca înainte și să obțină valoarea contabilă, trebuie să aducem și noi console.log în funcție, de asemenea.

function handleLikePost() {
  let likeCount = 0;
  likeCount += 1;
  console.log("like count:", likeCount);
}

handleLikePost(); // like count: 1

Și încă funcționează corect ca înainte.

Deci, acum utilizatorii ar trebui să poată aprecia o postare de câte ori doresc, așa că hai să apelăm handleLikePost de încă câteva ori:

handleLikePost(); // like count: 1
handleLikePost(); // like count: 1
handleLikePost(); // like count: 1

Cu toate acestea, când rulăm acest cod, există o problemă.

Ne-am aștepta să vedem likeCount continuați să creșteți, dar vedem doar 1 de fiecare dată. De ce este asta?

Ia o secundă, uită-te la codul nostru și încearcă să explici de ce likeCount nu mai este incrementat.

Să ne uităm la noi handleLikePost funcția și cum funcționează:

function handleLikePost() {
  let likeCount = 0;
  likeCount += 1;
  console.log("like count:", likeCount);
}

De fiecare dată când îl folosim, recreăm acest lucru likeCount variabilă, căreia i se dă o valoare inițială de 0.

Nu este de mirare că nu putem urmări numărul dintre apelurile de funcții! Se menține setat la 0 de fiecare dată, apoi este incrementat cu 1, după care funcția este terminată de rulare.

Așa că suntem blocați aici. Variabila noastră trebuie să trăiască în interiorul handleLikePost funcție, dar nu putem păstra numărul.

Avem nevoie de ceva care să ne permită să păstrăm sau să ne amintim likeCount valoare între apelurile funcționale.

Ce se întâmplă dacă am încerca ceva care poate părea puțin ciudat la început – ce se întâmplă dacă am încerca să punem o altă funcție în funcția noastră:

function handleLikePost() {
  let likeCount = 0;
  likeCount += 1;
  function() {

  }
}

handleLikePost();

Aici vom numi această funcție addLike. Motivul? Deoarece va fi responsabil pentru creșterea likeCount variabilă acum.

Și rețineți că această funcție interioară nu trebuie să aibă un nume. Poate fi o funcție anonimă. În majoritatea cazurilor, este. Îi dăm doar un nume, astfel încât să putem vorbi mai ușor despre el și despre ce face.

addLike va fi acum responsabil pentru creșterea noastră likeCount, așa că vom muta linia în care creștem cu 1 în funcția noastră interioară.

function handleLikePost() {
  let likeCount = 0;
  function addLike() {
    likeCount += 1;
  }
}

Dacă am numi asta addLike funcționează în handleLikePost?

Tot ce s-ar întâmpla este că addLike ne-ar crește likeCount, dar totuși likeCount variabila ar fi distrusă. Din nou, ne pierdem valoarea, iar rezultatul este 0.

Dar în loc să sune addLike în cadrul funcției sale de închidere, ce se întâmplă dacă l-am numi în afara funcției? Acest lucru pare și mai ciudat. Și cum am face asta?

Știm în acest moment că funcțiile returnează valori. De exemplu, am putea să ne întoarcem likeCount valoare la sfârșitul anului handleLikePost să îl transmiteți altor părți ale programului nostru:

function handleLikePost() {
  let likeCount = 0;
  function addLike() {
    likeCount += 1;
  }
  addLike();
  return likeCount;
}

Dar, în loc să facem asta, să ne întoarcem likeCount în addLike și apoi întoarceți addLike funcția în sine:

function handleLikePost() {
  let likeCount = 0;
  return function addLike() {
    likeCount += 1;
    return likeCount;
  };
  // addLike();
}

handleLikePost();

Acum, acest lucru poate părea bizar, dar acest lucru este permis în JS. Putem folosi funcții ca orice altă valoare în JS. Asta înseamnă că o funcție poate fi returnată dintr-o altă funcție. Prin returnarea funcției interioare, o putem numi din afara funcției sale de închidere.

Dar cum am face asta? Gândește-te la asta un minut și vezi dacă poți să-ți dai seama …

În primul rând, pentru a vedea mai bine ce se întâmplă, haideți console.log(handleLikePost) când îl sunăm și vedem ce obținem:

function handleLikePost() {
  let likeCount = 0;
  return function addLike() {
    likeCount += 1;
    return likeCount;
  };
}

console.log(handleLikePost()); // ƒ addLike()

În mod surprinzător, obținem addLike funcție înregistrată. De ce? Pentru că îl returnăm, la urma urmei.

Pentru a o numi, nu am putea să o punem într-o altă variabilă? După cum tocmai am spus, funcțiile pot fi utilizate ca orice altă valoare în JS. Dacă îl putem returna dintr-o funcție, îl putem pune și într-o variabilă. Deci, să o punem într-o nouă variabilă numită like:

function handleLikePost() {
  let likeCount = 0;
  return function addLike() {
    likeCount += 1;
    return likeCount;
  };
}

const like = handleLikePost();

Și, în sfârșit, să sunăm like. O vom face de câteva ori și console.log fiecare rezultat:

function handleLikePost() {
  let likeCount = 0;
  return function addLike() {
    likeCount += 1;
    return likeCount;
  };
}

const like = handleLikePost();

console.log(like()); // 1
console.log(like()); // 2
console.log(like()); // 3

Al nostru likeCount se păstrează în cele din urmă! De fiecare dată când sunăm like, likeCount este incrementat față de valoarea sa anterioară.

Deci, ce s-a întâmplat în lume aici? Ei bine, ne-am dat seama cum să apelăm la addLike funcție din afara domeniului în care a fost declarat. Am făcut acest lucru returnând funcția interioară din cea externă și stocând o referință la aceasta, numită like, să-i spun.

Cum funcționează o închidere, linie cu linie?

Deci, aceasta a fost implementarea noastră, desigur, dar cum am păstrat valoarea likeCount între apeluri funcționale?

function handleLikePost() {
  let likeCount = 0;
  return function addLike() {
    likeCount += 1;
    return likeCount;
  };
}

const like = handleLikePost();

console.log(like()); // 1
  1. handleLikePost funcția exterioară este executată, creând o instanță a funcției interioare addLike; acea funcție se închide peste variabilă likeCount, care este un scop mai sus.
  2. Am sunat la addLike funcție din afara domeniului în care a fost declarat. Am făcut acest lucru returnând funcția interioară din cea externă și stocând o referință la aceasta, numită like, să-i spun.
  3. Cand like funcția se termină, în mod normal ne-am aștepta ca toate variabilele sale să fie colectate la gunoi (eliminate din memorie, care este un proces automat pe care îl face compilatorul JS). Ne-am aștepta la fiecare likeCount să plece când funcția este terminată, dar nu o fac.

Care este acest motiv? Închidere.

Întrucât instanțele funcției interioare sunt încă vii (atribuite like), închiderea păstrează încă countLike variabile.

Ați crede că a avea o funcție scrisă într-o altă funcție ar fi ca o funcție scrisă în domeniul global. Dar nu este.

Acesta este motivul pentru care închiderea face funcțiile atât de puternice, deoarece este o proprietate specială care nu este prezentă în nimic altceva în limbă.

Durata de viață a unei variabile

Pentru a aprecia mai bine închiderile, trebuie să înțelegem cum tratează JavaScript variabilele create. Este posibil să vă fi întrebat ce se întâmplă cu variabilele atunci când închideți pagina sau mergeți la o altă pagină din cadrul unei aplicații. Cât trăiesc variabilele?

Variabilele globale sunt valabile până când programul este eliminat, de exemplu când închideți fereastra. Sunt în jur pentru viața programului.

Cu toate acestea, variabilele locale au o viață scurtă. Acestea sunt create atunci când funcția este invocată și șterse când funcția este terminată.

Deci înainte, unde likeCount a fost doar o variabilă locală, când funcția a fost rulată. Variabila likeCount a fost creată la începutul funcției și apoi distrusă odată ce a terminat executarea.

Închiderile nu sunt instantanee – păstrează variabile locale în viață

Se spune uneori că închiderile JavaScript sunt similare instantaneelor, o imagine a programului nostru la un moment dat. Aceasta este o concepție greșită pe care o putem disipa adăugând o altă caracteristică funcționalității butonului nostru similar.

Să presupunem că, în unele ocazii rare, vrem să le permitem utilizatorilor să „dubleze ca” o postare și să incrementeze likeCount cu 2 la un loc în loc de 1.

Cum am adăuga această caracteristică?

Un alt mod de a transmite valori unei funcții este, desigur, prin argumente, care funcționează la fel ca variabilele locale.

Să trecem într-un argument numit pas către funcție, care ne va permite să furnizăm o valoare dinamică, modificabilă, pentru a crește numărul nostru în locul valorii codificate 1.

function handleLikePost(step) {
  let likeCount = 0;
  return function addLike() {
    likeCount += step;
    // likeCount += 1;
    return likeCount;
  };
}

În continuare, să încercăm să realizăm o funcție specială care ne va permite să dublăm ca postările noastre, doubleLike. Vom trece în 2 ca al nostru step valoare pentru a o face și apoi încercați să apelați ambele funcții, like și doubleLike:

function handleLikePost(step) {
  let likeCount = 0;
  return function addLike() {
    likeCount += step;
    return likeCount;
  };
}

const like = handleLikePost(1);
const doubleLike = handleLikePost(2);

like(); // 1
like(); // 2

doubleLike(); // 2 (the count is still being preserved!)
doubleLike(); // 4

Vedem likeCount se păstrează și pentru doubleLike.

Ce se intampla aici?

Fiecare instanță din interior addLike funcția se închide peste ambele likeCount și step variabile din exteriorul său handleLikePost sfera funcției. step rămâne același în timp, dar numărul este actualizat la fiecare invocare a acelei funcții interioare. Deoarece închiderea depășește variabilele și nu doar instantanee ale valorilor, aceste actualizări sunt păstrate între apelurile funcționale.

Deci, ce ne arată acest cod – faptul că putem transmite valori dinamice pentru a schimba rezultatul funcției noastre? Că sunt încă în viață! Închiderile mențin variabile locale în funcție de funcțiile care ar fi trebuit să le distrugă cu mult timp în urmă.

Cu alte cuvinte, acestea nu sunt statice și neschimbătoare, ca un instantaneu al valorii variabilelor închise la un moment dat – închiderile păstrează variabilele și oferă o legătură activă cu acestea. Ca urmare, putem folosi închiderile pot observa sau face actualizări ale acestor variabile în timp.

Ce este o închidere, mai exact?

Acum că vedeți cum este utilă o închidere, există două criterii pentru ca ceva să fie o închidere, ambele pe care le-ați văzut aici:

  1. Închiderile sunt o proprietate a funcțiilor JavaScript și numai a funcțiilor. Niciun alt tip de date nu le are.
  2. Pentru a observa o închidere, trebuie să executați o funcție într-un domeniu diferit de cel în care a fost definită inițial acea funcție.

De ce ar trebui să știți închiderile?

Să răspundem la întrebarea inițială la care ne-am propus să răspundem. Pe baza a ceea ce am văzut, întrerupeți și luați o lovitură la răspunsul la această întrebare. De ce ar trebui să ne pese de închideri ca dezvoltatori JS?

Închiderile contează pentru dvs. și pentru codul dvs., deoarece vă permit să „vă amintiți” valorile, care este o caracteristică foarte puternică și unică în limba pe care numai funcțiile o posedă.

Am văzut-o chiar aici în acest exemplu. La urma urmei, la ce folosește o variabilă like count care nu-și amintește like-urile? Veți întâlni acest lucru adesea în cariera dvs. de JS. Trebuie să păstrați cumva o anumită valoare și probabil să o păstrați separată de alte valori. Ce folosesti? O functie. De ce? Pentru a ține evidența datelor în timp, cu o închidere.

Și, cu asta, sunteți deja cu un pas înaintea altor dezvoltatori.

Vrei să devii un maestru JS? Alăturați-vă JC Bootcamp 2020? ️

Alăturați-vă JC Bootcamp 2020

Urmăriți + Salutați! ? Stare de nervozitateInstagramcodeartistry.io