Closures in JavaScript

Closures in JavaScript

Dit artikel legt closures in JavaScript uit.

YouTube Video

Closures in JavaScript

In JavaScript is een 'closure' een van de zeer belangrijke en krachtige concepten. Door closures te begrijpen, kun je kennis opdoen die nuttig is in veel situaties, zoals het gedrag en de scope van functies, evenals de asynchrone verwerking en eventafhandeling in JavaScript. Hier leggen we in detail alles uit, van de basisdefinitie van closures tot concrete voorbeelden en hun toepassingen.

Wat is een Closure?

Een closure verwijst naar het mechanisme waarbij een functie toegang heeft tot variabelen uit de scope waarin hij is aangemaakt, zelfs wanneer hij buiten die scope wordt aangeroepen. Door closures te gebruiken, wordt het mogelijk voor functies om externe variabelen te blijven 'onthouden'.

Closures bestaan uit de volgende twee elementen.

  1. Functiedefinitie (de functie zelf)

  2. Scope waarin de functie is gedefinieerd (variabelen en andere functies buiten de functie zelf)

In JavaScript zijn closures mogelijk omdat functies toegang hebben tot variabelen binnen de scope waarin ze zijn aangemaakt.

Basisvoorbeeld

Laten we eerst kijken naar een basisvoorbeeld van een closure. In de volgende code retourneert outerFunction een functie genaamd innerFunction. Het belangrijke punt is dat innerFunction toegang heeft tot de variabele count, die gedefinieerd is in de scope van outerFunction.

 1function outerFunction() {
 2    let count = 0;
 3
 4    function innerFunction() {
 5        count++;
 6        console.log(`Current count: ${count}`);
 7    }
 8
 9    return innerFunction;
10}
11
12const counter = outerFunction();
13counter(); // Current count: 1
14counter(); // Current count: 2
15counter(); // Current count: 3

Hoe werken Closures

Zoals te zien is in het bovenstaande voorbeeld, wordt count door innerFunction vastgehouden, zelfs nadat outerFunction is uitgevoerd. innerFunction heeft blijvend toegang tot de scope van outerFunction, en daarom wordt count binnen innerFunction bijgewerkt. Dit is het basismechanisme van een closure.

Het is innerFunction die wordt toegewezen aan de variabele counter, en we zien dat de staat van count behouden blijft, ook al is outerFunction al uitgevoerd. Dit komt doordat JavaScript voortdurend de scope onthoudt op het moment van functiedefinitie.

Toepassing: Closures als Privé-variabelen

Closures kunnen worden gebruikt als 'privé-variabelen' in objectgeoriënteerd programmeren. Normaal gesproken zijn objecteigenschappen in JavaScript direct toegankelijk van buitenaf, maar door closures te gebruiken, kun je directe manipulatie van variabelen binnen de functie-scope van buitenaf voorkomen.

In het volgende voorbeeld gebruikt de functie createCounter een closure om een teller te maken en retourneert een teller met een privévariabele count.

 1function createCounter() {
 2    let count = 0;
 3
 4    return {
 5        increment: function() {
 6            count++;
 7            console.log(`Count: ${count}`);
 8        },
 9        decrement: function() {
10            count--;
11            console.log(`Count: ${count}`);
12        },
13        getCount: function() {
14            return count;
15        }
16    };
17}
18
19const myCounter = createCounter();
20myCounter.increment(); // Count: 1
21myCounter.increment(); // Count: 2
22myCounter.decrement(); // Count: 1
23console.log(myCounter.getCount()); // 1

In dit voorbeeld bevindt count zich in de scope van de functie createCounter, waardoor er niet direct van buitenaf toegang toe is. Het kan echter worden gemanipuleerd via de methoden increment en decrement. Op deze manier kun je met closures het concept van privé-variabelen in JavaScript toepassen.

Praktische Voorbeelden van Closures

Combinatie met Callback-functies

Closures worden vaak gebruikt in combinatie met callback-functies om asynchrone processen te beheren. Laten we bijvoorbeeld eens kijken naar een voorbeeld met een timer.

 1function startTimer(duration) {
 2    let timeLeft = duration;
 3
 4    function countdown() {
 5        console.log(`Time left: ${timeLeft} seconds`);
 6        timeLeft--;
 7
 8        if (timeLeft >= 0) {
 9            setTimeout(countdown, 1000);
10        }
11    }
12
13    countdown();
14}
15
16startTimer(5);
17// Time left: 5 seconds
18// Time left: 4 seconds
19// Time left: 3 seconds
20// Time left: 2 seconds
21// Time left: 1 second
22// Time left: 0 seconds

In dit voorbeeld krijgt de functie countdown toegang tot de variabele timeLeft binnen de scope van startTimer. Op deze manier zijn closures erg handig voor asynchrone processen zoals timers, omdat ze de status van variabelen over tijd behouden.

Eventhandlers

Closures zijn ook handig bij het opzetten van eventhandlers. In het volgende voorbeeld wordt een closure gebruikt om het aantal keer dat op een knop is geklikt bij te houden.

 1function setupClickCounter(buttonId) {
 2    let clickCount = 0;
 3
 4    const button = document.getElementById(buttonId);
 5    button.addEventListener('click', function() {
 6        clickCount++;
 7        console.log(`Button clicked ${clickCount} times`);
 8    });
 9}
10
11setupClickCounter('myButton');

In dit geval neemt clickCount toe bij elke klik en wordt de waarde ervan behouden. Met closures kun je elke knop een aparte teller geven.

Conclusie

Closures zijn een concept dat de flexibiliteit en kracht van JavaScript symboliseert. Ze bewaren variabelen binnen een functiescope en maken bewerkingen op die variabelen mogelijk via extern toegankelijke functies. Door dit mechanisme te begrijpen en te gebruiken, kun je geavanceerdere technieken in JavaScript ontwikkelen.

Closures worden in verschillende situaties toegepast, van eventafhandeling en asynchrone verwerking tot zelfs pseudo-implementaties van objectgeoriënteerd programmeren.

Je kunt het bovenstaande artikel volgen met Visual Studio Code op ons YouTube-kanaal. Bekijk ook het YouTube-kanaal.

YouTube Video