„JavaScript“ uždarymo pamoka - su JS uždarymo pavyzdžio kodu

Uždarymai - daugelis iš jūsų „JavaScript“ kūrėjų tikriausiai girdėjote šį terminą anksčiau. Pradėjęs savo kelionę naudodamas „JavaScript“, dažnai susidurdavau su uždarymu. Ir aš manau, kad tai viena iš svarbiausių ir įdomiausių „JavaScript“ sąvokų.

Nemanote, kad jie įdomūs? Tai dažnai nutinka, kai nesupranti sąvokos - tau neatrodo įdomu. (Nežinau, ar jums taip nutinka, ar ne, bet taip yra su manimi).

Taigi šiame straipsnyje pasistengsiu, kad uždarymai būtų įdomūs jums.

Prieš eidami į uždarymų pasaulį, pirmiausia supraskime leksinį aprėptį . Jei jau žinote apie tai, praleiskite kitą dalį. Priešingu atveju pereikite prie jo, kad geriau suprastumėte uždarymus.

Leksinis apimtys

Galbūt jūs galvojate - aš žinau vietinę ir pasaulinę apimtį, bet kokia gi yra leksinė sritis? Aš išgirdau šį terminą reagavau taip pat. Nesijaudinti! Pažvelkime atidžiau.

Tai paprasta, kaip ir kiti du taikymo sritis:

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

Iš aukščiau pateikto išėjimo galite pamatyti, kad vidinė funkcija gali pasiekti išorinės funkcijos kintamąjį. Tai yra leksinis aprėptis, kai kintamojo apimtį ir vertę lemia jo apibrėžimo / sukūrimo vieta (tai yra jo padėtis kode). Supratau?

Aš žinau, kad paskutinė dalis galėjo jus supainioti. Taigi leisk man jus gilinti. Ar žinojote, kad leksinis apimties nustatymas taip pat žinomas kaip statinis ? Taip, tai kitas jo vardas.

Taip pat yra dinaminis aprėptis , kurį palaiko kai kurios programavimo kalbos. Kodėl aš paminėjau dinaminį taikymo sritį? Nes tai gali padėti geriau suprasti leksinį aprėptį.

Pažvelkime į keletą pavyzdžių:

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

Ar sutinkate su išvestimi? Taip, tai suteiks nuorodos klaidą. Taip yra dėl to, kad abi funkcijos neturi prieigos viena prie kitos, nes jos yra apibrėžtos atskirai.

Pažvelkime į kitą pavyzdį:

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

Aukščiau išvardyta vertė bus 20 dinamiškai aprėpiamos kalbos. Kalbos, palaikančios leksinį aprėptį, duosreferenceError: number2 is not defined. Kodėl?

Kadangi taikant dinaminį mastelį, paieška pirmiausia vyksta vietinėje funkcijoje, tada ji pereina į funkciją, kuri tą vietinę funkciją iškvietė . Tada ji ieško toje funkcijoje, kuri iškvietė funkciją ir pan.

Jo pavadinimas savaime suprantamas - „dinamiškas“ reiškia pokyčius. Kintamojo apimtis ir vertė gali būti skirtingi, nes tai priklauso nuo to, kur paskambinta funkcija. Kintamojo reikšmė gali pasikeisti vykdymo metu.

Turi dinamiško taikymo srities esmę? Jei taip, tada tiesiog atsiminkite, kad leksinis aprėptis yra priešinga.

Taikant leksinį apimtį, paieška pirmiausia vyksta vietinėje funkcijoje, tada ji eina į funkciją, kurios viduje ši funkcija yra apibrėžta. Tada jis ieško funkcijos, kurioje ši funkcija yra apibrėžta, ir pan.

Taigi, leksinis ar statinis taikymo sritis reiškia, kad kintamojo apimtis ir vertė nustatoma iš ten, kur jis apibrėžtas. Tai nesikeičia.

Pažvelkime dar kartą į pirmiau pateiktą pavyzdį ir pabandykite patys išsiaiškinti išvestį. Tik vienas posūkis - deklaruokite number2viršuje:

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

Ar žinote, kokia bus išvestis?

Teisingai - leksikos apimties kalboms tai 12. Taip yra todėl, kad pirmiausia ji žiūri į addNumbersfunkciją (vidinę sritį), tada ieško į vidų, kur ši funkcija yra apibrėžta. Kai jis gauna number2kintamąjį, tai reiškia, kad išvestis yra 12.

Jums gali kilti klausimas, kodėl aš tiek laiko praleidau leksiniam aprėpimui. Tai baigiamasis straipsnis, ne apie leksinį aprėptį. Bet jei nežinote apie leksinį aprėptį, nesuprasite uždarymų.

Kodėl? Atsakymą gausite, kai pažvelgsime į uždarymo apibrėžimą. Taigi leiskime į trasą ir grįžkime prie uždarymų.

Kas yra uždarymas?

Pažvelkime į uždarymo apibrėžimą:

Uždarymas sukuriamas, kai vidinė funkcija turi prieigą prie savo išorinių funkcijų kintamųjų ir argumentų. Vidinė funkcija turi prieigą prie:

1. Savo kintamieji.

2. Išorinės funkcijos kintamieji ir argumentai.

3. Visuotiniai kintamieji.

Laukti! Ar tai yra uždarymo ar leksinio taikymo srities apibrėžimas? Abu apibrėžimai atrodo vienodi. Kuo jie skiriasi?

Na, todėl aukščiau apibrėžiau leksinį aprėptį. Nes uždarymai yra susiję su leksiniu / statiniu apimtimi.

Dar kartą pažvelkime į kitą jo apibrėžimą, kuris jums pasakys, kaip skiriasi uždarymai.

Uždarymas yra tada, kai funkcija gali pasiekti savo leksinę sritį, net kai ši funkcija vykdoma už leksinės srities ribų.

Arba

Vidinės funkcijos gali pasiekti jos pirminę sritį net ir tada, kai pirminė funkcija jau yra įvykdyta.

Sumišęs? Nesijaudinkite, jei dar negavote prasmės. Turiu pavyzdžių, kurie padės geriau suprasti. Pakeiskime pirmąjį leksinio taikymo srities pavyzdį:

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

Šis kodas skiriasi tuo, kad mes grąžiname vidinę funkciją ir ją vykdome vėliau. Kai kuriose programavimo kalbose vietinis kintamasis egzistuoja vykdant funkciją. Bet kai funkcija bus įvykdyta, tų vietinių kintamųjų nėra ir jie nebus prieinami.

Tačiau čia scena kitokia. Vykdžius pagrindinę funkciją, vidinė funkcija (grąžinta funkcija) vis tiek gali pasiekti pirminės funkcijos kintamuosius. Taip, jūs atspėjote teisingai. Uždarymai yra priežastis.

Vidinė funkcija išsaugo savo leksinę apimtį, kai vykdoma pagrindinė funkcija, taigi vėliau ši vidinė funkcija gali pasiekti tuos kintamuosius.

To get a better feel for it, let’s use the dir() method of the console to look into the list of the properties of callGreetCustomer:

console.dir(callGreetCustomer);

From the above image, you can see how the inner function preserves its parent scope (customerName) when greetCustomer() is executed. And later on, it used customerName when callGreetCustomer() was executed.

I hope this example helped you better understand the above definition of a closure. And maybe now you find closures a bit more fun.

So what next? Let’s make this topic more interesting by looking at different examples.

Examples of closures in action

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

Every time you call countValue, the count variable value is incremented by 1. Wait – did you think that the value of count is 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.

Viskas! Tikiuosi, kad dabar galite pasakyti, kad uždarymai atrodo įdomūs.

Norėdami perskaityti kitus mano straipsnius, peržiūrėkite mano profilį čia.