In de informatica is een closure een functie die een eigen omgeving heeft. In die omgeving bevinden zich één of meer gebonden variabelen: namen die een waarde hebben (bijvoorbeeld een getal, object of andere referentie). De omgeving van de closure bewaart die gebonden variabelen in het geheugen en zorgt ervoor dat de functie ze kan blijven gebruiken, ook nadat de plaats waarin de functie is gemaakt (bijvoorbeeld een andere functie) is beëindigd.
Het begrip closure kreeg die naam door Peter J. Landin in 1964. De programmeertaal Scheme maakte closures breed toepasbaar en populair vanaf ongeveer 1975. Sindsdien hebben veel moderne talen (zoals JavaScript, Python, Ruby en vele functionele talen) closures ingebouwd of ondersteunen ze patronen die hetzelfde effect geven.
Hoe een closure werkt
Belangrijke punten om te begrijpen bij closures:
- Lexicale scope: een closure legt de omgeving vast op het moment dat de functie wordt gedefinieerd. Dat betekent dat variabelen uit de omringende scope "meegaan" met de functie.
- Gebonden vs. vrije variabelen: een gebonden variabele is lokaal voor de closure; een vrije variabele is een naam die niet binnen de functie wordt gedefinieerd maar uit een buitenliggende scope komt en door de closure wordt vastgehouden.
- Levensduur van variabelen: omdat de closure een referentie naar de omgeving behoudt, blijven die variabelen bestaan zolang de closure zelf bereikbaar is. Dit is vaak beheerd door de garbage collector van de taal.
- By-reference of by-value: in sommige talen verwijst een closure naar de actuele variabele (late binding) en in andere talen worden waarden gekopieerd op het moment van aanmaak (early binding). Dit beïnvloedt hoe veranderingen van buitenaf zichtbaar zijn binnen de closure.
Voorbeelden (kort)
JavaScript (veelvoorkomend patroon: functie-fabriek)
function maakTeller() { let teller = 0; return function() { teller += 1; return teller; }; } const tel = maakTeller(); console.log(tel()); // 1 console.log(tel()); // 2 In dit voorbeeld houdt de anonieme functie de variabele teller vast tussen aanroepen. De binnenste functie is een closure omdat hij de omgeving van maakTeller bewaart.
Python (inner function)
def maak_adder(x): def adder(y): return x + y return adder add5 = maak_adder(5) print(add5(3)) # 8 Ook hier behoudt adder de waarde van x uit de buitenste scope.
Let op taalverschillen: in JavaScript zie je soms het probleem van late binding in loops; dat kan worden opgelost met blokscope (let) of door een extra functie te gebruiken.
Toepassingen
- Encapsulatie van staat zonder objecten (bijv. factory-functies).
- Callbacks en event-handlers die toegang nodig hebben tot lokale context.
- Partiële toepassing en currying in functionele programmeerstijlen.
- Implementatie van iterators en generator-achtige patronen.
Gevolgen voor geheugen en ontwerp
Closures kunnen handig zijn, maar ze houden ook verwijzingen naar omgeving vast. Dat kan onbedoeld geheugen langer vasthouden (bijvoorbeeld grote datastructuren of DOM-elementen in webapplicaties) als de closure niet wordt opgeruimd. Goede praktijk is om bewust te zijn van wat een closure vastlegt en, indien nodig, referenties vrij te geven of geen onnodig grote objecten te sluiten.
Veelvoorkomende misverstanden
Anonieme functies (functies zonder naam) worden soms ten onrechte synoniem gebruikt met closures. Veel talen die anonieme functies ondersteunen, bieden ook closures, maar de twee begrippen zijn niet hetzelfde. Een anonieme functie is alleen een closure als hij een eigen omgeving heeft met ten minste één gebonden variabele. Een anonieme functie zonder eigen omgeving is geen closure. Evenzo kan een closure een naam hebben; een named closure is dus niet per definitie anoniem.
Samengevat: een closure is een functie plus de omgeving die de functie nodig heeft om te kunnen werken buiten de oorspronkelijke scope. Closures zijn fundamenteel in moderne programmeerstijlen en bieden krachtige patronen voor het organiseren van staat, maar vereisen ook aandacht voor scope- en geheugenbeheer.