Hvad er en JavaScript-lukning? Venligst på almindelig engelsk.

Hver funktion i JavaScript har en lukning. Og dette er en af ​​de sejeste funktioner i JavaScript-sproget. For uden lukninger ville det være svært at implementere almindelige strukturer som tilbagekald eller begivenhedshåndterere.

Du opretter en lukning, når du definerer en funktion. Så når du udfører funktioner, giver deres lukning dem mulighed for at få adgang til data i deres rækkevidde.

Det er lidt ligesom når en bil er fremstillet (defineret) det kommer med et par funktioner som start, accelerate, decelerate. Disse bilfunktioner udføres af føreren hver gang de betjener bilen. Lukninger for disse funktioner er defineret med selve bilen, og de lukker over variabler, de har brug for til at betjene.

Lad os indsnævre denne analogi til acceleratefunktionen. Funktionsdefinitionen sker, når bilen er fremstillet:

function accelerate(force) { // Is the car started? // Do we have fuel? // Are we in traction control mode? // Many other checks... // If all good, burn more fuel depending on // the force variable (how hard we’re pressing the gas pedal) }

Hver gang føreren trykker på gaspedalen, udføres disse funktioner. Bemærk, hvordan denne funktion har brug for adgang til en masse variabler for at fungere, inklusive dens egen forcevariabel. Men vigtigere, det har brug for variabler uden for dets anvendelsesområde, der styres af andre bilfunktioner. Det er her, lukningen af acceleratefunktionen (som vi får med selve bilen) kommer til nytte.

Her er hvad acceleratefunktionens lukning lovede selve acceleratefunktionen:

Ok accelerate, når du bliver henrettet, kan du få adgang til din forcevariabel, du kan få adgang til isCarStartedvariablen, du kan også få adgang til fuelLevelvariablen og isTractionControlOnvariablen. Du kan også kontrollere den currentFuelSupplyvariabel, vi sender til motoren.

Bemærk, at lukningen ikke gav acceleratefunktionen faste værdier for disse variabler, men snarere tilladelse til at få adgang til disse værdier på det tidspunkt, hvor accelerationsfunktionen udføres.

Lukninger er tæt knyttet til funktionsomfang, så forståelse af, hvordan disse omfang fungerer, hjælper dig med at forstå lukninger. Kort sagt er det vigtigste at forstå om rækkevidde, at når du udfører en funktion, oprettes et privat funktionsomfang og bruges til processen med at udføre denne funktion.

Derefter bliver disse funktionsomfang indlejret, når du udfører funktioner inden for funktioner (hvilket du altid vil gøre).

En lukning oprettes, når du definerer en funktion - ikke når du udfører den. Hver gang du udfører denne funktion, giver dens allerede definerede lukning derefter adgang til alle de funktionsomfang, der er tilgængelige omkring den.

På en måde kan du tænke på rækkevidde som midlertidige (det globale omfang er den eneste undtagelse herfra), mens du kan tænke på lukninger som permanente.

For virkelig at forstå lukninger og den rolle, de spiller i JavaScript, skal du først forstå et par andre enkle koncepter om JavaScript-funktioner og deres omfang.

Før vi kommer i gang, skal du bemærke, at jeg også har oprettet et interaktivt laboratorium til dette, som du kan arbejde igennem her.

1 - Funktioner tildeles ved hjælp af værdireference

Når du sætter en funktion i en variabel som denne:

function sayHello() { console.log("hello"); }; var func = sayHello;

Du tildeler variablen funcen henvisning til funktionen sayHello, ikke en kopi. Her funcer simpelthen et alias til sayHello. Alt hvad du laver på aliaset, gør du faktisk på den oprindelige funktion. For eksempel:

func.answer = 42; console.log(sayHello.answer); // prints 42

Ejendommen answerblev sat direkte påfuncog læse ved hjælp af sayHello, hvilket fungerer.

Du kan også udføre sayHelloved at udføre funcaliaset:

func() // prints "hello"

2 - Omfang har en levetid

Når du ringer til en funktion, opretter du et omfang under udførelsen af ​​denne funktion. Så forsvinder dette omfang.

Når du kalder funktionen en anden gang, opretter du et nyt andet omfang under den anden udførelse. Så forsvinder også dette andet omfang.

function printA() { console.log(answer); var answer = 1; }; printA(); // this creates a scope which gets discarded right after printA(); // this creates a new different scope which also gets discarded right after;

Disse to anvendelsesområder, der blev oprettet i eksemplet ovenfor, er forskellige. Variablen answerher deles slet ikke mellem dem.

Hvert funktionsomfang har en levetid. De bliver skabt, og de kasseres med det samme. Den eneste undtagelse fra dette faktum er det globale anvendelsesområde, der ikke forsvinder, så længe applikationen kører.

3 - Lukninger spænder over flere anvendelsesområder

Når du definerer en funktion, oprettes en lukning

Unlike scopes, closures are created when you define a function, not when you execute it. Closures also don’t go away after you execute that function.

You can access the data in a closure long after a function is defined and after it gets executed as well.

A closures encompasses everything the defined function can access. This means the defined function’s scope, and all the nested scopes between the global scope and the defined function scope plus the global scope itself.

var G = 'G'; // Define a function and create a closure function functionA() { var A = 'A' // Define a function and create a closure function functionB() { var B = 'B' console.log(A, B, G); } functionB(); // prints A, B, G // functionB closure does not get discarded A = 42; functionB(); // prints 42, B, G } functionA();

When we define functionB here, its created closure will allow us to access the scope of functionB plus the scope of functionA plus the global scope.

Every time we execute functionB, we can access variables B, A, and Ggennem dets tidligere oprettede lukning. Denne lukning giver os imidlertid ikke en kopi af disse variabler, men snarere en henvisning til dem. Så hvis for eksempel variablenes værdi Aændres på et eller andet tidspunkt, efter at lukningen functionBer oprettet, når vi udfører functionBefter det, ser vi den nye værdi, ikke den gamle. Det andet opkald til functionBudskrifter42, B, Gfordi værdien af ​​variablen Ablev ændret til 42 og lukningen gav os en henvisning til A, ikke en kopi.

Forveks ikke lukninger med rækkevidde

Det er almindeligt, at lukninger forveksles med rækkevidde, så lad os sørge for ikke at gøre det.

// scope: global var a = 1; void function one() { // scope: one // closure: [one, global] var b = 2; void function two() { // scope: two // closure: [two, one, global] var c = 3; void function three() { // scope: three // closure: [three, two, one, global] var d = 4; console.log(a + b + c + d); // prints 10 }(); }(); }();

I det enkle eksempel ovenfor har vi tre funktioner, og de defineres alle og straks påberåbes, så de skaber alle omfang og lukninger.

The scope of function one() is its body. Its closure gives us access to both its scope and the global scope.

The scope of function two() is its body. Its closure gives us access to its scope plus the scope of function one()plus the global scope

And similarly, the closure of function three() gives us access to all scopes in the example. This is why we were able to access all variables in function three().

But the relation between scopes and closures is not always simple like this. Things become different when the defining and invoking of functions happen in different scopes. Let me explain that with an example:

var v = 1; var f1 = function () { console.log(v); } var f2 = function() { var v = 2; f1(); // Will this print 1 or 2? }; f2();

What do you think the above example will print? The code is simple, f1() prints the value of v, which is 1 on the global scope, but we execute f1() inside of f2(), which has a different v that’s equal to 2. Then we execute f2().

Will this code print 1 or 2?

If you’re tempted to say 2, you’ll be surprised. This code will actually print 1. The reason is, scopes and closures are different. The console.log line will use the closure of f1(), which is created when we define f1(), which means the closure of f1() gives us access to only the scope of f1() plus the global scope. The scope where we execute f1() does not affect that closure. In fact, the closure of f1() will not give us access to the scope of f2() at all. If you remove the global v variable and execute this code, you’ll get a reference error:

var f1 = function () { console.log(v); } var f2 = function() { var v = 2; f1(); // ReferenceError: v is not defined }; f2();

This is very important to understand and remember.

4 — Closures have read and write access

Since closures give us references to variables in scopes, the access that they give us means both read and write, not just read.

Take a look at this example:

function outer() { let a = 42; function inner() { a = 43; } inner(); console.log(a); } outer();

The inner() function here, when defined, creates a closure that gives us access to the variable a. We can read and modify that variable, and if we do modify it, we will be modifying the actual a variable in the outer() scope.

This code will print 43 because we used the inner() function closure to modify the outer() function variable.

This is actually why we can change global variables everywhere. All closures give us both read and write access to all global variables.

5 — Closures can share scopes

Since closures give us access to nested scopes at the time we define functions, when we define multiple functions in the same scope, that scope is shared among all created closures, and of course, because of this, the global scope is always shared among all closures.

function parent() { let a = 10; function double() { a = a+a; console.log(a); }; function square() { a = a*a; console.log(a); } return { double, square } } let { double, square } = parent(); double(); // prints 20 square(); // prints 400 double(); // prints 800

In the example above, we have a parent() function with variable a set to 10. We define two functions in this parent() function’s scope, double() and square(). The closures created for double() and square() both share the scope of the parent() function. Since both double() and square() change the value of a, when we execute the last 3 lines, we double a (making a = 20), then square that doubled value (making a = 400), then double that squared value (making a = 800).

En sidste test

Lad os nu kontrollere din forståelse af lukninger hidtil. Inden du udfører følgende kode, skal du prøve at gætte, hvad den udskriver:

let a = 1; const function1 = function() { console.log(a); a = 2 } a = 3; const function2 = function() { console.log(a); } function1(); function2();

Jeg håber, du fik det rigtigt, og jeg håber, at disse enkle begreber hjælper dig med virkelig at forstå den vigtige rolle, som funktionslukninger spiller i JavaScript.

Tak for læsningen.

Learning React eller Node? Tjek mine bøger:

  • Lær React.js ved at bygge spil
  • Node.js ud over det grundlæggende