JavaScript Closure Tutorial - Mit JS Closure-Beispielcode

Closures - viele von euch JavaScript-Entwicklern haben diesen Begriff wahrscheinlich schon einmal gehört. Als ich meine Reise mit JavaScript begann, stieß ich häufig auf Schließungen. Und ich denke, sie sind eines der wichtigsten und interessantesten Konzepte in JavaScript.

Sie finden sie nicht interessant? Dies passiert oft, wenn Sie ein Konzept nicht verstehen - Sie finden es nicht interessant. (Ich weiß nicht, ob dir das passiert oder nicht, aber das ist bei mir der Fall).

In diesem Artikel werde ich versuchen, Verschlüsse für Sie interessant zu machen.

Bevor wir in die Welt der Schließungen einsteigen, wollen wir zunächst das lexikalische Scoping verstehen . Wenn Sie bereits davon wissen, überspringen Sie den nächsten Teil. Andernfalls springen Sie hinein, um Verschlüsse besser zu verstehen.

Lexikalisches Scoping

Sie denken vielleicht - ich kenne den lokalen und globalen Geltungsbereich, aber was zum Teufel ist der lexikalische Geltungsbereich? Ich habe genauso reagiert, als ich diesen Begriff hörte. Keine Sorgen machen! Lass uns genauer hinschauen.

Es ist einfach wie bei den beiden anderen Bereichen:

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

Sie können der obigen Ausgabe entnehmen, dass die innere Funktion auf die Variable der äußeren Funktion zugreifen kann. Dies ist ein lexikalischer Bereich, bei dem der Umfang und der Wert einer Variablen davon abhängen, wo sie definiert / erstellt wird (dh von ihrer Position im Code). Ich habs?

Ich weiß, dass das letzte bisschen dich verwirrt haben könnte. Also lass mich dich tiefer bringen. Wussten Sie, dass lexikalisches Scoping auch als statisches Scoping bezeichnet wird ? Ja, das ist der andere Name.

Es gibt auch dynamisches Scoping , das einige Programmiersprachen unterstützen. Warum habe ich dynamisches Scoping erwähnt? Weil es Ihnen helfen kann, das lexikalische Scoping besser zu verstehen.

Schauen wir uns einige Beispiele an:

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

Stimmen Sie der Ausgabe zu? Ja, es wird ein Referenzfehler ausgegeben. Dies liegt daran, dass beide Funktionen keinen Zugriff auf den Gültigkeitsbereich des anderen haben, da sie separat definiert werden.

Schauen wir uns ein anderes Beispiel an:

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

Die obige Ausgabe ist 20 für eine Sprache mit dynamischem Gültigkeitsbereich. Sprachen, die das lexikalische Scoping unterstützen, werden gebenreferenceError: number2 is not defined. Warum?

Da beim dynamischen Scoping die Suche zuerst in der lokalen Funktion und dann in der Funktion erfolgt, die diese lokale Funktion aufgerufen hat . Dann sucht es in der Funktion, die diese Funktion aufgerufen hat , usw. im Aufrufstapel.

Sein Name ist selbsterklärend - „dynamisch“ bedeutet Veränderung. Der Umfang und der Wert der Variablen können unterschiedlich sein, da dies davon abhängt, wo die Funktion aufgerufen wird. Die Bedeutung einer Variablen kann sich zur Laufzeit ändern.

Haben Sie den Kern des dynamischen Scoping? Wenn ja, dann denken Sie daran, dass das lexikalische Scoping das Gegenteil ist.

Beim lexikalischen Scoping findet die Suche zuerst in der lokalen Funktion statt, dann in der Funktion, in der diese Funktion definiert ist. Dann sucht es in der Funktion, in der diese Funktion definiert ist und so weiter.

So, lexikalische oder statische Scoping Weise des Umfangs und der Wert einer Variablen bestimmt wird , von wo sie definiert ist. Es ändert sich nicht.

Schauen wir uns noch einmal das obige Beispiel an und versuchen Sie, die Ausgabe selbst herauszufinden. Nur eine Wendung - oben deklarieren number2:

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

Wissen Sie, wie die Ausgabe aussehen wird?

Richtig - es ist 12 für Sprachen mit lexikalischem Umfang. Dies liegt daran, dass zuerst eine addNumbersFunktion (innerster Bereich) untersucht und dann nach innen gesucht wird, wo diese Funktion definiert ist. Wenn es die number2Variable erhält , bedeutet dies, dass die Ausgabe 12 ist.

Sie fragen sich vielleicht, warum ich hier so viel Zeit mit lexikalischem Scoping verbracht habe. Dies ist ein Abschlussartikel, nicht einer über lexikalisches Scoping. Wenn Sie jedoch nichts über lexikalisches Scoping wissen, werden Sie Verschlüsse nicht verstehen.

Warum? Sie erhalten Ihre Antwort, wenn wir uns die Definition eines Abschlusses ansehen. Also lasst uns auf die Strecke gehen und zu den Schließungen zurückkehren.

Was ist eine Schließung?

Schauen wir uns die Definition eines Verschlusses an:

Closure wird erstellt, wenn eine innere Funktion Zugriff auf ihre äußeren Funktionsvariablen und Argumente hat. Die innere Funktion hat Zugriff auf -

1. Seine eigenen Variablen.

2. Variablen und Argumente der äußeren Funktion.

3. Globale Variablen.

Warten! Ist dies die Definition eines Verschlusses oder eines lexikalischen Geltungsbereichs? Beide Definitionen sehen gleich aus. Wie unterscheiden sie sich?

Nun, deshalb habe ich oben das lexikalische Scoping definiert. Weil Verschlüsse mit lexikalischem / statischem Scoping zusammenhängen.

Schauen wir uns noch einmal die andere Definition an, die Ihnen zeigt, wie unterschiedlich die Verschlüsse sind.

Schließen ist, wenn eine Funktion auf ihren lexikalischen Bereich zugreifen kann, selbst wenn diese Funktion außerhalb ihres lexikalischen Bereichs ausgeführt wird.

Oder,

Innere Funktionen können auf den übergeordneten Bereich zugreifen, auch nachdem die übergeordnete Funktion bereits ausgeführt wurde.

Verwirrt? Mach dir keine Sorgen, wenn du den Punkt noch nicht verstanden hast. Ich habe Beispiele, die Ihnen helfen, besser zu verstehen. Lassen Sie uns das erste Beispiel für lexikalisches Scoping ändern:

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

Der Unterschied in diesem Code besteht darin, dass wir die innere Funktion zurückgeben und später ausführen. In einigen Programmiersprachen ist die lokale Variable während der Ausführung der Funktion vorhanden. Sobald die Funktion ausgeführt wurde, sind diese lokalen Variablen jedoch nicht mehr vorhanden und können nicht mehr aufgerufen werden.

Hier ist die Szene jedoch anders. Nachdem die übergeordnete Funktion ausgeführt wurde, kann die innere Funktion (zurückgegebene Funktion) weiterhin auf die Variablen der übergeordneten Funktion zugreifen. Ja, du hast richtig geraten. Schließungen sind der Grund.

Die innere Funktion behält ihren lexikalischen Bereich bei, wenn die übergeordnete Funktion ausgeführt wird, und daher kann diese innere Funktion später auf diese Variablen zugreifen.

Um ein besseres Gefühl dafür zu bekommen, verwenden wir die dir()Methode der Konsole, um in die Liste der Eigenschaften von callGreetCustomer:

console.dir(callGreetCustomer);

Aus dem obigen Bild können Sie sehen, wie die innere Funktion ihren übergeordneten Bereich ( customerName) beibehält, wenn sie greetCustomer()ausgeführt wird. Und später wurde es verwendet, customerNameals callGreetCustomer()es ausgeführt wurde.

Ich hoffe, dieses Beispiel hat Ihnen geholfen, die obige Definition eines Abschlusses besser zu verstehen. Und vielleicht finden Sie jetzt Verschlüsse ein bisschen lustiger.

Wie geht es weiter? Lassen Sie uns dieses Thema anhand verschiedener Beispiele interessanter gestalten.

Beispiele für Schließungen in Aktion

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

Bei jedem Aufruf countValuewird der Wert der Zählvariablen um 1 erhöht. Warten Sie - haben Sie gedacht, dass der Wert von count 0 ist?

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.

That’s it! I hope you can now say that you find closures interesting.

To read my other articles, check out my profile here.