JavaScript-lukningsvejledning - Med JS-lukningseksempelkode

Lukninger - mange af jer JavaScript-devs har sandsynligvis hørt dette udtryk før. Da jeg startede min rejse med JavaScript, stødte jeg ofte på lukninger. Og jeg synes, de er et af de vigtigste og mest interessante koncepter i JavaScript.

Tror du ikke, de er interessante? Dette sker ofte, når du ikke forstår et koncept - du finder det ikke interessant. (Jeg ved ikke, om dette sker for dig eller ej, men det er tilfældet med mig).

Så i denne artikel vil jeg forsøge at gøre lukninger interessante for dig.

Før vi går ind i en verden af ​​lukninger, lad os først forstå leksikalsk scoping . Hvis du allerede ved om det, skal du springe den næste del over. Ellers spring ind i det for bedre at forstå lukninger.

Lexikal Scoping

Du tænker måske - jeg kender lokalt og globalt omfang, men hvad er det leksikale omfang? Jeg reagerede på samme måde, da jeg hørte dette udtryk. Ikke at bekymre sig! Lad os se nærmere på.

Det er simpelt som andre to anvendelsesområder:

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

Du kan se fra ovenstående output, at den indre funktion kan få adgang til den ydre funktions variabel. Dette er leksikalsk scoping, hvor omfanget og værdien af ​​en variabel bestemmes af, hvor den er defineret / oprettet (det vil sige dens position i koden). Forstået?

Jeg ved, at sidste bit måske har forvirret dig. Så lad mig tage dig dybere. Vidste du, at leksikalskoping også er kendt som statisk scoping ? Ja, det er dets andet navn.

Der er også dynamisk scoping , som nogle programmeringssprog understøtter. Hvorfor har jeg nævnt dynamisk scoping? Fordi det kan hjælpe dig med bedre at forstå leksikalsk scoping.

Lad os se på nogle eksempler:

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

Er du enig i output? Ja, det vil give en referencefejl. Dette skyldes, at begge funktioner ikke har adgang til hinandens omfang, da de er defineret separat.

Lad os se på et andet eksempel:

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

Ovenstående output vil være 20 for et dynamisk afgrænset sprog. Sprog, der understøtter leksikalsk scoping, giverreferenceError: number2 is not defined. Hvorfor?

Fordi i dynamisk scoping finder søgning først sted i den lokale funktion, så går det ind i den funktion, der kaldte den lokale funktion. Derefter søger den i den funktion, der kaldte den funktion og så videre, op i opkaldsstakken.

Navnet er selvforklarende - "dynamisk" betyder ændring. Omfanget og værdien af ​​variablen kan være forskellig, da det afhænger af, hvorfra funktionen kaldes. Betydningen af ​​en variabel kan ændres ved kørselstid.

Har du kernen i dynamisk scoping? Hvis ja, så husk bare, at leksikalsk scoping er det modsatte.

Ved leksikalsk scoping finder søgning først sted i den lokale funktion, hvorefter den går ind i den funktion, inden for hvilken denne funktion er defineret. Derefter søger den i den funktion, inden for hvilken den funktion er defineret, og så videre.

leksikal eller statisk afgrænsning betyder, at omfanget og værdien af ​​en variabel bestemmes fra det sted, hvor den er defineret. Det ændrer sig ikke.

Lad os igen se på ovenstående eksempel og prøve at finde ud af output på egen hånd. Bare et twist - erklær number2øverst:

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

Ved du hvad output vil være?

Korrekt - det er 12 for leksikalt afgrænsede sprog. Dette skyldes, at det først ser på en addNumbersfunktion (inderste omfang), så søger den indad, hvor denne funktion er defineret. Når den får number2variablen, betyder det, at output er 12.

Du undrer dig måske over, hvorfor jeg har brugt så meget tid på leksikalsk scoping her. Dette er en afslutningsartikel, ikke en artikel om leksikalsk scoping. Men hvis du ikke kender til leksikalsk scoping, vil du ikke forstå lukninger.

Hvorfor? Du får dit svar, når vi ser på definitionen af ​​en lukning. Så lad os komme ind på banen og komme tilbage til lukninger.

Hvad er en lukning?

Lad os se på definitionen af ​​en lukning:

Lukning oprettes, når en indre funktion har adgang til dens ydre funktionsvariabler og argumenter. Den indre funktion har adgang til -

1. Dens egne variabler.

2. Ydre funktions variabler og argumenter.

3. Globale variabler.

Vente! Er dette definitionen af ​​en lukning eller en leksikalsk scoping? Begge definitioner ser ens ud. Hvordan er de forskellige?

Nå, det er derfor, jeg definerede leksikalsk scoping ovenfor. Fordi lukninger er relateret til leksikal / statisk scoping.

Lad os igen se på den anden definition, der fortæller dig, hvordan lukninger er forskellige.

Lukning er, når en funktion er i stand til at få adgang til sit leksikale omfang, selv når denne funktion udføres uden for sit leksikale anvendelsesområde.

Eller,

Indre funktioner kan få adgang til dets overordnede rækkevidde, selv efter at den overordnede funktion allerede er udført.

Forvirret? Bare rolig, hvis du endnu ikke har fået pointen. Jeg har eksempler, der hjælper dig med bedre at forstå. Lad os ændre det første eksempel på leksikalsk scoping:

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

Forskellen i denne kode er, at vi returnerer den indre funktion og udfører den senere. I nogle programmeringssprog findes den lokale variabel under funktionens udførelse. Men når funktionen er udført, findes disse lokale variabler ikke, og de vil ikke være tilgængelige.

Her er scenen dog anderledes. Når den overordnede funktion er udført, kan den indre funktion (returneret funktion) stadig få adgang til overordnede funktionens variabler. Ja, du gættede rigtigt. Lukninger er årsagen.

Den indre funktion bevarer sit leksikale omfang, når den overordnede funktion udføres, og senere kan den indre funktion senere få adgang til disse variabler.

For at få en bedre fornemmelse af det, lad os bruge dir()metoden til konsollen til at se på listen over egenskaberne for callGreetCustomer:

console.dir(callGreetCustomer);

Fra ovenstående billede kan du se, hvordan den indre funktion bevarer sit overordnede omfang ( customerName), når den greetCustomer()udføres. Og senere brugte den, customerNameda den callGreetCustomer()blev henrettet.

Jeg håber, at dette eksempel hjalp dig med bedre at forstå ovenstående definition af en lukning. Og måske finder du lukninger lidt sjovere nu.

Så hvad nu? Lad os gøre dette emne mere interessant ved at se på forskellige eksempler.

Eksempler på lukninger i aktion

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

Hver gang du ringer countValue, øges tællingsvariabelværdien med 1. Vent - troede du, at værdien af ​​tællingen er 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.

Det er det! Jeg håber, du nu kan sige, at du finder lukninger interessante.

For at læse mine andre artikler, se min profil her.