Închideri – mulți dintre voi dezvoltatorii de JavaScript au auzit probabil acest termen înainte. Când mi-am început călătoria cu JavaScript, am întâlnit închideri des. Și cred că sunt unul dintre cele mai importante și interesante concepte din JavaScript.

Nu crezi că sunt interesante? Acest lucru se întâmplă adesea atunci când nu înțelegeți un concept – nu vi se pare interesant. (Nu știu dacă ți se întâmplă asta sau nu, dar acesta este cazul meu).

Deci, în acest articol, voi încerca să fac închiderile interesante pentru dvs.

Înainte de a intra în lumea închiderilor, să înțelegem mai întâi sfera lexicală. Dacă știți deja despre aceasta, săriți următoarea parte. Altfel săriți în el pentru a înțelege mai bine închiderile.

Domeniul de aplicare lexical

S-ar putea să vă gândiți – Știu domeniul de aplicare local și global, dar ce naiba este domeniul de aplicare lexical? Am reacționat la fel când am auzit acest termen. Să nu vă faceți griji! Să aruncăm o privire mai atentă.

Este simplu ca alte două domenii:

function greetCustomer() {
    var customerName = "anchal";
    function greetingMsg() {
	  console.log("Hi! " + customerName); // Hi! anchal
    }
   greetingMsg();
}

Puteți vedea din ieșirea de mai sus că funcția interioară poate accesa variabila funcției externe. Acesta este domeniul lexical, în care sfera și valoarea unei variabile sunt determinate de locul în care este definită / creată (adică poziția sa în cod). Am înțeles?

Știu că ultimul pic te-ar fi putut confunda. Așa că lasă-mă să te duc mai adânc. Știați că sfera lexicală este cunoscută și sub numele de scopuri statice? Da, acesta este celălalt nume al său.

Există deasemenea scop dinamic, pe care unele limbaje de programare le acceptă. De ce am menționat domeniul de aplicare dinamic? Deoarece vă poate ajuta să înțelegeți mai bine domeniul de aplicare lexical.

Să vedem câteva exemple:

function greetingMsg() {
  console.log(customerName);// ReferenceError: customerName is not defined
}

function greetCustomer() {
   var customerName = "anchal";
   greetingMsg();
}

greetCustomer();

Sunteți de acord cu rezultatul? Da, va da o eroare de referință. Acest lucru se datorează faptului că ambele funcții nu au acces la sfera celuilalt, deoarece sunt definite separat.

Să vedem un alt exemplu:

function addNumbers(number1) {
  console.log(number1 + number2);
}

function addNumbersGenerate() {
  var number2 = 10;
  addNumbers(number2);
}

addNumbersGenerate();

Ieșirea de mai sus va fi 20 pentru un limbaj cu scop dinamic. Limbile care susțin sfera lexicală vor da referenceError: number2 is not defined. De ce?

Deoarece în scop dinamic, căutarea are loc mai întâi în funcția locală, apoi intră în funcția care numit acea funcție locală. Apoi caută în funcția care a apelat acea funcția și așa mai departe, în sus, în teancul de apeluri.

Numele său se explică de la sine – „dinamic” înseamnă schimbare. Domeniul de aplicare și valoarea variabilei pot fi diferite, deoarece depinde de unde este apelată funcția. Semnificația unei variabile se poate schimba în timpul rulării.

Ai esența scopului dinamic? Dacă da, amintiți-vă doar că scopul lexical este opusul său.

În domeniul lexical, căutarea are loc mai întâi în funcția locală, apoi intră în funcția în care acea funcția este definită. Apoi caută în funcția în care acea funcția este definită și așa mai departe.

Asa de, lexical sau scopuri statice înseamnă că scopul și valoarea unei variabile sunt determinate de unde este definită. Nu se schimbă.

Să ne uităm din nou la exemplul de mai sus și să încercăm să aflăm rezultatul pe cont propriu. Doar o întorsătură – declară number2 în vârf:

var number2 = 2;
function addNumbers(number1) {
  console.log(number1 + number2);
}

function addNumbersGenerate() {
  var number2 = 10;
  addNumbers(number2);
}

addNumbersGenerate();

Știți care va fi rezultatul?

Corect – este 12 pentru limbile cu scop lexical. Acest lucru se datorează faptului că mai întâi, se uită într-un addNumbers funcție (sfera cea mai interioară), apoi caută spre interior, unde este definită această funcție. Pe măsură ce devine number2 variabilă, adică ieșirea este 12.

S-ar putea să vă întrebați de ce am petrecut atât de mult timp pe domeniul lexical aici. Acesta este un articol de închidere, nu unul despre domeniul lexical. Dar dacă nu știți despre domeniul lexical, atunci nu veți înțelege închiderile.

De ce? Veți primi răspunsul dvs. atunci când ne uităm la definiția unei închideri. Deci, hai să intrăm pe pistă și să revenim la închideri.

Ce este o închidere?

Să ne uităm la definiția unei închideri:

Închiderea este creată atunci când o funcție interioară are acces la variabilele și argumentele funcției sale externe. Funcția interioară are acces la –
1. Variabile proprii.
2. Variabilele și argumentele funcției externe.
3. Variabile globale.

Aștepta! Aceasta este definiția unei închideri sau a unui domeniu lexical? Ambele definiții arată la fel. Cum sunt diferiți?

Ei bine, de aceea am definit mai sus sfera lexicală. Deoarece închiderile sunt legate de sfera lexicală / statică.

Să ne uităm din nou la cealaltă definiție a acesteia, care vă va spune în ce fel sunt diferite închiderile.

Închiderea este atunci când o funcție își poate accesa sfera lexicală, chiar și atunci când acea funcție se execută în afara sferei sale lexicale.

Sau,

Funcțiile interioare își pot accesa domeniul de aplicare părinte, chiar și după ce funcția părinte este deja executată.

Confuz? Nu vă faceți griji dacă nu ați reușit încă să înțelegeți. Am exemple care să vă ajute să înțelegeți mai bine. Să modificăm primul exemplu de scop lexical:

function greetCustomer() {
  const customerName = "anchal";
  function greetingMsg() {
    console.log("Hi! " + customerName);
  }
  return greetingMsg;
}

const callGreetCustomer = greetCustomer();
callGreetCustomer(); // output – Hi! anchal

Diferența în acest cod este că returnăm funcția interioară și o executăm mai târziu. În unele limbaje de programare, variabila locală există în timpul executării funcției. Dar odată ce funcția este executată, acele variabile locale nu există și nu vor fi accesibile.

Aici, însă, scena este diferită. După executarea funcției părinte, funcția interioară (funcția returnată) poate accesa în continuare variabilele funcției părinte. Da, ai ghicit. Închiderile sunt motivul.

Funcția interioară își păstrează sfera lexicală atunci când funcția părinte se execută și, prin urmare, ulterior această funcție interioară poate accesa acele variabile.

Pentru a avea o senzație mai bună, să folosim dir() metoda consolei pentru a căuta în lista proprietăților callGreetCustomer:

console.dir(callGreetCustomer);
Tutorial de inchidere JavaScript Cu cod de exemplu de

Din imaginea de mai sus, puteți vedea cum funcția interioară își păstrează domeniul de aplicare părinte (customerName) cand greetCustomer() este executat. Și mai târziu, s-a folosit customerName cand callGreetCustomer() a fost executat.

Sper că acest exemplu te-a ajutat să înțelegi mai bine definiția de mai sus a unei închideri. Și poate că acum găsești închideri ceva mai distractive.

Deci ce urmează? Să facem acest subiect mai interesant, examinând diferite exemple.

Exemple de închideri în acțiune

function counter() {
  let count = 0;
  return function() {
    return count++;
  };
}

const countValue = counter();
countValue(); // 0
countValue(); // 1
countValue(); // 2

De fiecare dată când suni countValue, valoarea variabilei de numărare este mărită cu 1. Așteptați – ați crezut că valoarea numărării este 0?

Ei bine, ar fi greșit, deoarece o închidere nu funcționează cu o valoare. Acesta stochează referinţă a variabilei. De aceea, când actualizăm valoarea, aceasta se reflectă în al doilea sau al treilea apel și așa mai departe, deoarece închiderea stochează referința.

Te simți puțin mai clar acum? Să vedem un alt exemplu:

function counter() {
  let count = 0;
  return function () {
    return count++;
  };
}

const countValue1 = counter();
const countValue2 = counter();
countValue1();  // 0
countValue1();  // 1
countValue2();   // 0
countValue2();   // 1

Sper că ai ghicit răspunsul corect. Dacă nu, iată motivul. La fel de countValue1 și countValue2, ambele își păstrează propriul domeniu lexical. Au medii lexicale independente. Poți să folosești dir() pentru a verifica [[scopes]] valoare în ambele cazuri.

Să ne uităm la un al treilea exemplu.

Acesta este puțin diferit. În el, trebuie să scriem o funcție pentru a obține rezultatul:

const addNumberCall = addNumber(7);
addNumberCall(8) // 15
addNumberCall(6) // 13

Simplu. Folosiți-vă cunoștințele de închidere recent dobândite:

function addNumber(number1) {
  return function (number2) {
    return number1 + number2;
  };
}

Să vedem acum câteva exemple complicate:

function countTheNumber() {
  var arrToStore = [];
  for (var x = 0; x < 9; x++) {
    arrToStore[x] = function () {
      return x;
    };
  }
  return arrToStore;
}

const callInnerFunctions = countTheNumber();
callInnerFunctions[0]() // 9
callInnerFunctions[1]() // 9

Fiecare element de matrice care stochează o funcție vă va oferi o ieșire de 9. Ați ghicit corect? Sper că da, dar totuși permiteți-mi să vă spun motivul. Acest lucru se datorează comportamentului închiderii.

Închiderea stochează referinţă, nu valoarea. Prima dată când rulează bucla, valoarea lui x este 0. Apoi a doua oară x este 1 și așa mai departe. Deoarece închiderea stochează referința, de fiecare dată când rulează bucla, schimbă valoarea lui x. Și în cele din urmă, valoarea lui x va fi 9. Deci callInnerFunctions[0]() dă o ieșire de 9.

Dar dacă doriți o ieșire de la 0 la 8? Simplu! Folosiți o închidere.

Gândiți-vă la asta înainte de a analiza soluția de mai jos:

function callTheNumber() {
  function getAllNumbers(number) {
    return function() {
      return number;
    };
  }
  var arrToStore = [];
  for (var x = 0; x < 9; x++) {
    arrToStore[x] = getAllNumbers(x);
  }
  return arrToStore;
}

const callInnerFunctions = callTheNumber();
console.log(callInnerFunctions[0]()); // 0
console.log(callInnerFunctions[1]()); // 1

Aici, am creat un domeniu de aplicare separat pentru fiecare iterație. Poți să folosești console.dir(arrToStore) pentru a verifica valoarea lui x în [[scopes]] pentru diferite elemente de matrice.

Asta e! Sper că acum puteți spune că vi se par interesante închiderile.

Pentru a citi celelalte articole ale mele, consultați profilul meu aici.