Hoe JavaScript-sluitingen werken, in gewoon Nederlands
JavaScript is een veelgebruikte taal waarmee je van alles kunt bouwen, van een eenvoudige landingspagina tot een full-stack productietoepassing. Naarmate JavaScript en programmeren in het algemeen zich verder ontwikkelden, realiseerden ontwikkelaars zich dat het paradigma van objectgeoriënteerd programmeren (OOP) voor de meeste toepassingen onwenselijk is. Functioneel programmeren kwam naar voren als de oplossing voor veel van de pijnpunten van OOP.
Closures zijn een veelbesproken onderwerp in de wereld van functioneel programmeren, maar ze worden vaak losjes en in technisch jargon gedefinieerd. We zullen hier ons best doen om in lekentaal uit te leggen hoe JavaScript closures werken.
Aan het eind van deze tutorial moet je het volgende begrijpen:
- Hoe je closures herkent
- Wat een closure is en hoe deze zich gedraagt ten opzichte van de uitvoeringscontext en de aanroepstack
- Common use cases voor closures
Uitleg JavaScript closures
We beginnen met te laten zien hoe een closure eruitziet.
Probeer de code maar eens zelf uit te voeren. Technisch gezien retourneert de functie met de naam makeCounter
een andere functie met de naam increment
. Deze increment
functie heeft toegang tot de count
variabele, zelfs nadat de makeCount
functie is uitgevoerd. Een deel van de sluiting is hier de count
variabele; deze is beschikbaar voor de increment
functie wanneer deze is gedefinieerd, zelfs nadat makeCounter
is voltooid. Het andere deel is de increment
functie.
Stel je voor dat je een huis hebt en een tuin die er omheen ligt. Zodra je de deur naar de tuin opent en sluit, kun je hem niet meer openen – de tuin wordt ontoegankelijk. Je hebt honger en gelukkig staan er een sinaasappelboom en een appelboom in je tuin. Je neemt een zakje, plukt een sinaasappel en een appel, en gaat terug je huis in. Je kunt niet meer naar buiten
Nu, als je eenmaal in je huis bent, kun je de sinaasappel of appel uit het zakje pakken en opeten wanneer je weer honger krijgt. Het kleine zakje in dit voorbeeld is de sluiting. Een closure bevat alle variabelen en functies die je tot je beschikking had toen je in de tuin was, ook als je binnen in het huis bent en niet meer naar buiten kunt.
Laten we eens kijken hoe dit in code uitpakt:
Omdat de fruits
variabele beschikbaar is voor de teruggegeven functie wanneer makeFruitGarden
wordt uitgevoerd, worden de fruits
variabele en de binnenste functie de closure. Wanneer consumeFruit
wordt uitgevoerd, wordt een fruit
– het laatste element uit de fruits
array omdat pop()
wordt gebruikt – geretourneerd. Als beide vruchten zijn geconsumeerd/opgegeten, blijft er niets meer over om op te eten.
Lexical scope
Om closures echt te begrijpen, moet je bekend zijn met de term “scope.” Lexical scope is een fancy term voor de huidige omgeving ten opzichte van datgene waar je naar verwijst.
In het volgende voorbeeld wordt de scope van de variabele met de naam myName
de “global scope” genoemd.
// global scopeconst myName = "John Doe"function displayName() { // local/function scope console.log(myName);};displayName()
Je hebt dit concept misschien al gezien toen je las over hoe var
niet block-scoped is en hoe const
let
dat wel is. Het is belangrijk op te merken dat in JavaScript, een functie altijd zijn eigen bereik creëert. Dit wordt de local
of function
scope genoemd, zoals te zien is in het codevoorbeeld.
Als je goed hebt opgelet, denk je misschien dat myName
en displayName
onderdeel zijn van een closure. En dan heb je gelijk! Maar omdat de functie en de variabele hier in het globale bereik bestaan, heeft het niet veel zin om het een closure te noemen.
Er zijn veel soorten scopes in JavaScript, maar als het op closures aankomt, zijn er drie scopes die je moet kennen:
- Het globale bereik is het standaard bereik waar iedereen in woont. Zie het als je straat
- De outer function scope is de functie die een functie retourneert. Het heeft zijn eigen bereik. Zie het als je tuin
- De binnenste/lokale functie scope is de teruggegeven functie die een sluiting wordt. Zie het als je huis
Nu duiken we in een aantal use-cases.
Common use-cases voor closures
Currying
Function currying is een ander krachtig concept in functioneel programmeren. Om een curried functie in JavaScript te implementeren, zou je closures gebruiken.
Currying van een functie kan worden beschreven als het transformeren van een functie en wordt als volgt uitgevoerd: add(1, 2, 3)
naar add(1)(2)(3)
.
function add(a) { return function(b) { return function(c) { return a + b + c; }; };};add(1)(2)(3) // returns 6
De add
functie neemt een enkel argument en geeft dan twee functies terug die achter elkaar genest zijn. Het doel van currying is om een heleboel argumenten te nemen en uiteindelijk te eindigen met een enkele waarde.
Hogere-orde functies
Het doel van een hogere-orde functie is om een functie als argument te nemen en een resultaat terug te geven. Array-methoden zoals map
en reduce
zijn voorbeelden van hogere-orde functies.
const arrayOfNumbers = ;const displayNumber = (num) => { console.log(num);}arrayOfNumbers.forEach(displayNumber)
De Array.prototype.forEach
hogere-orde functie hier accepteert displayNumber
als argument en voert het vervolgens uit voor elk element in de arrayOfNumbers
. Als je een UI-framework zoals Vue of React hebt gebruikt, ben je misschien bekend met componenten van hogere orde, die in wezen hetzelfde zijn als functies van hogere orde.
Wat is nu het verschil tussen functies van hogere orde en currying? Terwijl een hogere-orde functie een functie als argument neemt en een waarde retourneert, retourneert een curried functie een functie als resultaat, wat uiteindelijk tot een waarde leidt.
DOM element managers
Dit is een veelgebruikt ontwerppatroon dat vaak wordt gebruikt om eigenschappen van DOM-elementen te krijgen en in te stellen. In het volgende voorbeeld maken we een element manager om elementen te stijlen.
makeStyleManager
retourneert een object dat toegang geeft tot twee functies, die deel uitmaken van een closure naast de element
en currentStyles
variabelen. Zelfs nadat makeStyleManager
klaar is met uitvoeren, hebben de getStyle
en setStyle
functies toegang tot de variabelen.
Conclusie
JavaScript closures kunnen moeilijk te begrijpen zijn, zelfs voor ontwikkelaars met professionele ervaring in hun bagage. Inzicht in closures maakt je uiteindelijk een betere ontwikkelaar.
Je zou nu in staat moeten zijn om een closure te herkennen wanneer die wordt gebruikt in een codebase die er vreemd uitziet of nergens op slaat. Closures zijn een cruciaal concept in functioneel programmeren en ik hoop dat deze gids je heeft geholpen een stap voorwaarts te zetten op je weg om het onder de knie te krijgen.
LogRocket: JavaScript-fouten eenvoudiger debuggen door de context te begrijpen
Het debuggen van code is altijd een vervelende taak. Maar hoe beter u uw fouten begrijpt, hoe gemakkelijker het is om ze op te lossen.
LogRocket stelt u in staat deze fouten op nieuwe en unieke manieren te begrijpen. Onze frontend-monitoringoplossing volgt de betrokkenheid van gebruikers bij uw JavaScript-frontends, zodat u precies kunt achterhalen wat de gebruiker heeft gedaan dat tot een fout heeft geleid.
LogRocket registreert console-logs, paginalaadtijden, stacktraces, langzame netwerkverzoeken/reacties met headers + bodies, browsermetagegevens en aangepaste logboeken. Inzicht in de impact van je JavaScript-code is nog nooit zo eenvoudig geweest!
Probeer het gratis.
Verder lezen
- “Ik heb JavaScript closures nooit begrepen” – Een diepgaande, technische gids om te begrijpen hoe een closure werkt. Ideaal voor iemand die closures wil begrijpen met betrekking tot de
ExecutionContext
, call stack, lexicale omgeving, etc. - MDN reference for JavaScript closures – Een snelle referentiegids voor closures (inclusief enkele veelvoorkomende valkuilen) voor het geval je je geheugen snel moet bijspijkeren
- “Master the JavaScript Interview: Wat is een afsluiting? – Closures uitgelegd door Eric Elliot; zeer geschikt voor ontwikkelaars met een goed begrip van functionele programmeerconcepten