Inom datavetenskap är en closure en funktion som har en egen miljö. I denna miljö finns det minst en bunden variabel (ett namn som har ett värde, t.ex. ett tal). I stängningens miljö hålls de bundna variablerna i minnet mellan användningarna av stängningen.
Peter J. Landin gav denna idé namnet closure 1964. Programmeringsspråket Scheme gjorde closures populära efter 1975. Många programmeringsspråk som skapats efter den tiden har closures.
Anonyma funktioner (funktioner utan namn) kallas ibland felaktigt för closures. De flesta språk som har anonyma funktioner har också closures. En anonym funktion är också en closure om den har en egen miljö med minst en bunden variabel. En anonym funktion utan egen miljö är inte en closure. En namngiven closure är inte anonym.
Vad menas med "miljö" och "bunden variabel"?
Miljön är de variabler och deras värden som är tillgängliga för funktionen när den skapades. En bunden variabel är en variabel som inte är definierad inuti den inre funktionen utan i den omgivande kontexten (det yttre funktionsanropet eller omgivande blocket). När den inre funktionen refererar till sådana variabler säger man att den fångar dem — det är detta som gör funktionen till en closure.
Lexikal skopning (lexical scoping)
Closures bygger normalt på lexikal skopning, dvs. variabler binds utifrån var koden skrevs, inte var den körs. Det innebär att en funktion alltid ser de variabler som fanns i dess omgivning när den definierades, även om funktionen körs senare, i ett annat sammanhang.
Enkla exempel
Nedan följer exempel i JavaScript och Python som visar hur closures används i praktiken.
// JavaScript: räknefunktion som minns sitt eget tillstånd function makeCounter() { let count = 0; return function() { count++; return count; }; } const c = makeCounter(); console.log(c()); // 1 console.log(c()); // 2 # Python: funktioner som fångar variabler från omgivningen def make_counter(): count = 0 def counter(): nonlocal count count += 1 return count return counter c = make_counter() print(c()) # 1 print(c()) # 2 Vanliga fallgropar
En typisk fälla uppstår när man skapar funktioner i loopar och tror att varje funktion får sin egen kopia av loopvariabeln. I vissa språk (eller med gamla konstruktioner) delas bindningen, vilket leder till oväntade resultat.
// JavaScript (problematiskt i äldre kod med var) var funcs = []; for (var i = 0; i < 3; i++) { funcs.push(function() { return i; }); } console.log(funcs[0]()); // 3 (alla returnerar 3) Lösningar är att använda blockscoped variabler (let i modern JavaScript) eller skapa en wrapperfunktion som binder värdet:
// JavaScript med let (varje iteration får egen i) let funcs = []; for (let i = 0; i < 3; i++) { funcs.push(function() { return i; }); } console.log(funcs[0]()); // 0 # Python: "late binding" i lambdas inom loop funcs = [] for i in range(3): funcs.append(lambda: i) print(funcs[0]()) # 2 (alla returnerar 2) # Lösning: bind värdet som standardargument funcs = [] for i in range(3): funcs.append(lambda i=i: i) print(funcs[0]()) # 0 När är en anonym funktion en closure?
En anonym funktion blir en closure först när den fångar värden från sin omgivning. Exempelvis är en anonym funktion som enbart räknar med sina egna lokala parametrar inte en closure. Däremot blir den en closure om den refererar till variabler utanför sig själv som måste bevaras mellan anrop.
Användningsområden
- Data-inkapsling: Closures kan skapa privata variabler och funktioner (enkelt sätt att implementera information hiding utan klasser).
- Callbacks och asynkron kod: Closures används ofta när man skickar funktioner som callback som behöver komma åt viss kontext.
- Delvis applicering och currying: Skapa nya funktioner med en del av argumenten förifyllda.
- Generatorer och iteratorer: Bevara körstatus mellan anrop (i språk utan inbyggda generatorer används closures för detta).
Prestanda och minne
Eftersom en closure håller referenser till variabler i sin miljö kan dessa variabler inte samlas upp av skräpsamlare innan closure inte längre används. Det är oftast inte ett problem, men i långlivade closures med stora strukturer kan det leda till högre minnesanvändning. Var därför medveten om vad som fångas — ibland räcker det att fånga ett primitivt värde istället för en hel datastruktur.
Skillnaden mellan closure och anonym funktion — kortfattat
En anonym funktion är bara en funktion utan namn. En closure är en funktion (anonym eller namngiven) tillsammans med dess bevarade omgivning. Alla closures är funktioner, men inte alla funktioner är closures.
Sammanfattning
Closures är ett kraftfullt och vanligt verktyg i modern programmering som låter funktioner bära med sig sin omgivning. De möjliggör privat tillstånd, flexibla callbacks och funktionell stil som partial application. Samtidigt kräver de att man är medveten om variabelbindningar och livstidsaspekter för att undvika buggar och onödig minnesanvändning.