JavaScripti sulgemise õpetus - JS-i sulgemise näidiskoodiga

Sulgemine - paljud teist JavaScripti arendajatest on seda terminit ilmselt varem kuulnud. Kui alustasin oma reisi JavaScripti abil, kohtasin sageli sulgemisi. Ja ma arvan, et need on JavaScripti kõige olulisemad ja huvitavamad mõisted.

Arvad, et need pole huvitavad? See juhtub sageli siis, kui te ei saa mõistest aru - see ei tundu teile huvitav. (Ma ei tea, kas see juhtub teiega või mitte, aga minu puhul on see nii).

Nii et proovin selles artiklis sulgemised teie jaoks huvitavaks muuta.

Enne sulgemismaailma minemist mõistame kõigepealt leksikaalset ulatuse määramist . Kui juba teate, jätke järgmine osa vahele. Muidu hüpake sellesse, et sulgemisi paremini mõista.

Leksikaalne ulatus

Te võite mõelda - ma tean kohalikku ja globaalset ulatust, aga mis pagana leksikaalne ulatus on? Ma reageerisin seda terminit kuuldes samamoodi. Mitte muretseda! Vaatame lähemalt.

See on lihtne nagu teised kaks reguleerimisala:

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

Ülaltoodud väljundist näete, et sisemine funktsioon pääseb juurde välise funktsiooni muutujale. See on leksikaalne ulatus, kus muutuja ulatus ja väärtus määratakse kindlaks selle määramise / loomise koha (st selle asukoha järgi koodis) järgi. Sain aru?

Ma tean, et viimane natuke võis teid segadusse ajada. Nii et las ma viin teid sügavamale. Kas teadsite, et leksikaalset ulatust tuntakse ka staatilise ulatusena ? Jah, see on selle teine ​​nimi.

Samuti on olemas dünaamiline ulatus , mida mõned programmeerimiskeeled toetavad. Miks ma mainisin dünaamilist ulatuse määramist? Sest see aitab teil paremini mõista leksikaalset ulatuse määramist.

Vaatame mõningaid näiteid:

function greetingMsg() { console.log(customerName);// ReferenceError: customerName is not defined } function greetCustomer() { var customerName = "anchal"; greetingMsg(); } greetCustomer();

Kas olete väljundiga nõus? Jah, see annab võrdlusvea. Seda seetõttu, et mõlemal funktsioonil puudub juurdepääs üksteise ulatusele, kuna need on määratletud eraldi.

Vaatame veel ühte näidet:

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

Dünaamilise ulatusega keele puhul on ülaltoodud väljund 20. Leksikaalset ulatust toetavad keeled annavadreferenceError: number2 is not defined. Miks?

Kuna dünaamilises ulatuses toimub otsimine kõigepealt kohalikus funktsioonis, siis läheb see funktsiooni, mis kutsus seda kohalikku funktsiooni. Seejärel otsib see funktsiooni, mis selle funktsiooni kutsus , ja nii edasi, kuni kõne virna.

Selle nimi on iseenesestmõistetav - dünaamiline tähendab muutusi. Muutuja ulatus ja väärtus võivad olla erinevad, kuna see sõltub sellest, kust funktsiooni kutsutakse. Muutuja tähendus võib tööajal muutuda.

Kas teil on dünaamilise ulatusega ülevaade? Kui jah, siis pidage lihtsalt meeles, et leksikaalne ulatus on vastupidine.

Leksikaalses ulatuses toimub otsimine kõigepealt kohalikus funktsioonis, seejärel läheb see funktsiooni, mille sees see funktsioon on määratletud. Seejärel otsib see funktsioonist, milles see funktsioon on määratletud, ja nii edasi.

Niisiis, leksikaalse või staatiline kindlaksmääramisest tähendab ulatus ja muutuja väärtust määratakse, kui on määratletud. See ei muutu.

Vaatame uuesti ülaltoodud näidet ja proovime väljundit ise välja selgitada. Ainult üks väände - kuulutage number2ülaosas:

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

Kas teate, milline on väljund?

Õige - leksikaalselt reguleeritud keelte puhul on see 12. Seda seetõttu, et kõigepealt uuritakse addNumbersfunktsiooni (sisemine ulatus), seejärel otsitakse sissepoole, kus see funktsioon on määratletud. Nagu ta saab number2muutuja, mis tähendab väljund on 12.

Teile võib tekkida küsimus, miks ma olen siin leksikaalsele ulatuse määramisele nii palju aega kulutanud. See on sulgemisartikkel, mitte leksikaalse ulatuse kohta. Aga kui te ei tea leksikaalsest ulatusest, ei saa te sulgemisest aru.

Miks? Oma vastuse saate siis, kui vaatame sulgemise määratlust. Nii et lähme rajale ja pöördume tagasi sulgemiste juurde.

Mis on sulgemine?

Vaatame sulguri määratlust:

Sulgemine luuakse, kui sisemisel funktsioonil on juurdepääs oma välimise funktsiooni muutujatele ja argumentidele. Sisemisel funktsioonil on juurdepääs -

1. Oma muutujad.

2. Välisfunktsiooni muutujad ja argumendid.

3. Globaalsed muutujad.

Oota! Kas see on sulgemise või leksikaalse ulatuse määratlus? Mõlemad määratlused näevad välja ühesugused. Kuidas nad erinevad?

Noh, sellepärast määratlesin eespool leksikaalse ulatuse. Sest sulgemised on seotud leksikaalse / staatilise ulatusega.

Vaatame uuesti selle muud määratlust, mis ütleb teile, kuidas sulgemised erinevad.

Sulgemine on siis, kui funktsioon pääseb juurde oma leksikaalsele ulatusele, isegi kui see funktsioon töötab väljaspool leksikaalset ulatust.

Või

Sisemistel funktsioonidel on juurdepääs vanema ulatusele ka pärast seda, kui vanemfunktsioon on juba täidetud.

Segaduses? Ärge muretsege, kui te pole veel mõtet saanud. Mul on näiteid, mis aitavad teil paremini mõista. Muutkem leksikaalse ulatuse esimest näidet:

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

Selle koodi erinevus seisneb selles, et tagastame sisemise funktsiooni ja täidame selle hiljem. Mõnes programmeerimiskeeles eksisteerib lokaalne muutuja funktsiooni täitmise ajal. Kuid kui funktsioon on täidetud, pole neid kohalikke muutujaid olemas ja neile pole juurdepääsu.

Siin on aga stseen teistsugune. Pärast vanemfunktsiooni täitmist pääseb sisemine funktsioon (tagastatud funktsioon) ikkagi juurde vanemfunktsiooni muutujatele. Jah, sa arvasid õigesti. Põhjuseks on sulgemised.

Sisemine funktsioon säilitab oma leksikaalse ulatuse, kui vanemfunktsioon on käivitatud, ja seega saab hiljem see sisemine funktsioon nendele muutujatele juurde pääseda.

Selle paremaks tundmiseks kasutame dir()konsooli meetodit, et uurida järgmiste omaduste loendit callGreetCustomer:

console.dir(callGreetCustomer);

Ülaltoodud pildilt näete, kuidas sisemine funktsioon täidab oma vanema ulatuse ( customerName) greetCustomer(). Ja hiljem kasutati seda customerNamesiis, kui callGreetCustomer()see hukati.

Loodan, et see näide aitas teil eespool nimetatud sulgemise määratlust paremini mõista. Ja võib-olla leiate nüüd, et sulgemised on natuke lõbusamad.

Mis siis edasi saab? Teeme selle teema huvitavamaks, vaadeldes erinevaid näiteid.

Näited sulgemistest töös

function counter() { let count = 0; return function() { return count++; }; } const countValue = counter(); countValue(); // 0 countValue(); // 1 countValue(); // 2

Iga kord, kui helistate countValue, suurendatakse loenduse muutuja väärtust 1. Oodake - kas arvasite, et loenduse väärtus on 0?

Well, that would be wrong as a closure doesn’t work with a value. It stores the reference of the variable. That’s why, when we update the value, it reflects in the second or third call and so on as the closure stores the reference.

Feeling a bit clearer now? Let’s look at another example:

function counter() { let count = 0; return function () { return count++; }; } const countValue1 = counter(); const countValue2 = counter(); countValue1(); // 0 countValue1(); // 1 countValue2(); // 0 countValue2(); // 1 

I hope you guessed the right answer. If not, here is the reason. As countValue1 and countValue2, both preserve their own lexical scope. They have independent lexical environments. You can use dir() to check the [[scopes]] value in both the cases.

Let’s look at a third example.

This one's a bit different. In it, we have to write a function to achieve the output:

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

Simple. Use your newly-gained closure knowledge:

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

Now let’s look at some tricky examples:

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

Every array element that stores a function will give you an output of 9. Did you guess right? I hope so, but still let me tell you the reason. This is because of the closure's behavior.

The closure stores the reference, not the value. The first time the loop runs, the value of x is 0. Then the second time x is 1, and so on. Because the closure stores the reference, every time the loop runs it's changing the value of x. And at last, the value of x will be 9. So callInnerFunctions[0]() gives an output of 9.

But what if you want an output of 0 to 8? Simple! Use a closure.

Think about it before looking at the solution below:

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

Here, we have created separate scope for each iteration. You can use console.dir(arrToStore) to check the value of x in [[scopes]] for different array elements.

See selleks! Loodetavasti võite nüüd öelda, et sulgemised tunduvad teile huvitavad.

Minu teiste artiklite lugemiseks vaadake minu profiili siin.