Nu og da ser jeg “lukninger” blive nævnt, og jeg forsøgte at finde det, men Wiki giver ikke en forklaring, som jeg forstår. Kunne nogen hjælp mig herude?
Kommentarer
- Hvis du kender Java / C # håber dette link hjælper- http://www.developerfusion.com/article/8251/the-beauty-of-closures/
- Lukninger er svære at forstå. Du skal prøve at klikke på alle linkene i første sætning i den Wikipedia-artikel og forstå dem artikler først.
- stackoverflow.com/questions/36636/what-is-a-closure
- Hvad ‘ er den grundlæggende forskel mellem en lukning og en klasse dog? Okay, en klasse med kun en offentlig metode.
- @biziclop: Du kunne efterlign en lukning med en klasse (at ‘ er, hvad Java-devs skal gøre). Men de ‘ er normalt lidt mindre detaljerede at oprette, og du don behøver ikke at administrere det manuelt, hvad du ‘ holder øje med. (Hardcore-lisperne stiller et lignende spørgsmål, men når formodentlig den anden konklusion – at OO-understøttelse på sprogniveau er unødvendig, når du har lukninger).
Svar
(Ansvarsfraskrivelse: dette er en grundlæggende forklaring; for så vidt definitionen går, forenkler jeg lidt)
Den mest enkle måde at tænke på en lukning er en -funktion, der kan lagres som en variabel (kaldet en “første -klassefunktion “), der har en særlig mulighed for at få adgang til andre lokale variabler til det anvendelsesområde, den blev oprettet i.
Eksempel (JavaScript):
var setKeyPress = function(callback) { document.onkeypress = callback; }; var initialize = function() { var black = false; document.onclick = function() { black = !black; document.body.style.backgroundColor = black ? "#000000" : "transparent"; } var displayValOfBlack = function() { alert(black); } setKeyPress(displayValOfBlack); }; initialize();
Funktionerne 1 tildelt document.onclick
og displayValOfBlack
er lukninger. Du kan se, at de begge henviser til den boolske variabel black
, men den variabel er tildelt uden for funktionen. Fordi black
er lokal i det omfang, hvor funktionen blev defineret , bevares markøren til denne variabel.
Hvis du placerer dette på en HTML-side:
- Klik for at skifte til sort
- Tryk på [enter] for at se “sand”
- Klik igen, skifter tilbage til hvid
- Tryk på [enter] for at se “falsk”
Dette viser, at begge har adgang til det samme black
, og kan bruges til at gemme tilstand uden noget indpakningsobjekt.
Opkaldet til setKeyPress
er for at demonstrere, hvordan en funktion kan overføres ligesom enhver variabel. scope bevaret i lukningen er stadig den, hvor funktionen blev defineret.
Lukninger er almindeligt bruges som begivenhedshåndterere, især i JavaScript og ActionScript. God brug af lukninger hjælper dig med implicit at binde variabler til begivenhedshåndterere uden at skulle oprette et objektindpakning. Uforsigtig brug vil dog føre til hukommelseslækager (f.eks. Når en ubrugt, men bevaret begivenhedshåndterer er den eneste ting, der skal holdes fast i store genstande i hukommelsen, især DOM-objekter, hvilket forhindrer affaldsopsamling).
1: Faktisk er alle funktioner i JavaScript lukkede.
Kommentarer
- Da jeg læste dit svar, sagde jeg følte en lyspære tænde i mit sind. Meget værdsat! 🙂
- Da
black
erklæres inde i en funktion, ville ‘ ikke blive ødelagt, når stakken afvikles. ..? - @gablin, det er det, der er unikt ved sprog, der har lukninger. Alle sprog med affaldsindsamling fungerer stort set på samme måde – når der ikke holdes flere referencer til et objekt, kan det ødelægges. Hver gang en funktion oprettes i JS, er det lokale omfang bundet til den funktion, indtil den funktion er ødelagt.
- @gablin, det ‘ er et godt spørgsmål. Jeg tror ikke ‘ de kan ‘ t & mdash; men jeg bragte kun affaldssamling op, da det JS bruger, og at ‘ er det, du syntes at henvise til, da du sagde ” Siden
black
erklæres inde i en funktion, ville ikke ‘ t blive ødelagt “. Husk også, at hvis du erklærer et objekt i en funktion og derefter tildeler det til en variabel, der lever et andet sted, bevares det, fordi der er andre referencer til det. - Objective-C (og C under clang) understøtter blokke, som i det væsentlige er lukninger uden affaldsopsamling. Det kræver runtime support og nogle manuelle indgreb omkring hukommelsesstyring.
Svar
En lukning er grundlæggende bare en anden måde at se på et objekt. Et objekt er data, der har en eller flere funktioner bundet til sig. En lukning er en funktion, der har en eller flere variabler bundet til sig. De to er dybest set identiske, i det mindste på et implementeringsniveau. Den virkelige forskel er i, hvor de kommer fra.
I objektorienteret programmering erklærer du en objektklasse ved at definere dens medlemsvariabler og dens metoder (medlemsfunktioner) foran, og derefter opretter du forekomster af den klasse. Hver instans leveres med en kopi af medlemsdataene initialiseret af konstruktøren. Derefter har du en variabel af en objekttype og sender den rundt som et stykke data, fordi fokus er på dens natur som data.
I en lukning er objektet derimod ikke defineret foran som en objektklasse eller instantieret gennem et konstruktøropkald i din kode. I stedet skriver du lukningen som en funktion inde i en anden funktion. Lukningen kan henvise til en hvilken som helst af de ydre funktions lokale variabler, og compileren registrerer det og flytter disse variabler fra den ydre funktions stakplads til lukningens skjulte objekterklæring. Du har derefter en variabel af en lukningstype , og selvom det dybest set er et objekt under emhætten, sender du det rundt som en funktionsreference, fordi fokus er på dets natur som en funktion.
Kommentarer
- +1: Godt svar. Du kan se en lukning som et objekt med kun en metode og et vilkårligt objekt som en samling af lukninger over nogle almindelige underliggende data (objektets ‘ -variabler). Jeg synes, at disse to synspunkter er ret symmetriske.
- Meget godt svar. Det forklarer faktisk indsigt i lukning.
- @Mason Wheeler: Hvor gemmes lukningsdata? I stak som en funktion? Eller i bunke som et objekt?
- @RoboAlex: I bunken, fordi det ‘ er et objekt, der ligner en funktion .
- @RoboAlex: Hvor en lukning og dens fangede data gemmes, afhænger af implementeringen. I C ++ kan den gemmes i bunken eller på stakken.
Svar
Udtrykket lukning kommer fra det faktum, at et stykke kode (blok, funktion) kan have gratis variabler, der er lukket (dvs. bundet til en værdi) af det miljø, hvor kodeblokken er defineret.
Tag for eksempel definitionen af Scala-funktion :
def addConstant(v: Int): Int = v + k
I funktionsdelen er der to navne (variabler) v
og k
angiver to heltal værdier. Navnet v
er bundet, fordi det erklæres som et argument for funktionen addConstant
(ved at se på funktionserklæringen ved vi, at v
tildeles en værdi, når funktionen påkaldes). Navnet k
er gratis med funktionen addConstant
, fordi funktionen ikke indeholder nogen anelse om, hvilken værdi k
er bundet til (og hvordan).
For at evaluere et opkald som:
val n = addConstant(10)
skal vi tildele k
en værdi, som kun kan ske, hvis navnet k
er defineret i den sammenhæng, hvor addConstant
er defineret. For eksempel:
def increaseAll(values: List[Int]): List[Int] = { val k = 2 def addConstant(v: Int): Int = v + k values.map(addConstant) }
Nu hvor vi har defineret addConstant
i en sammenhæng, hvor k
er defineret, addConstant
er blevet en lukning fordi alle dens frie variabler er nu lukket (bundet til en værdi): addConstant
kan påberåbes og videregives som om det var en funktion. Bemærk, at den frie variabel k
er bundet til en værdi, når lukningen er defineret , hvorimod argumentvariablen v
er bundet, når lukningen er påkrævet .
Så en lukning er grundlæggende en funktion eller kodeblok, der kan få adgang til ikke-lokale værdier gennem sine gratis variabler, efter at disse er bundet af konteksten.
På mange sprog, hvis du Brug kun en lukning, når du kan gøre det anonym , f.eks.
def increaseAll(values: List[Int]): List[Int] = { val k = 2 values.map(v => v + k) }
Bemærk, at en funktion uden gratis variabler er et specielt tilfælde af en lukning (med et tomt sæt gratis variabler). Analogt er en anonym funktion et specielt tilfælde af en anonym lukning , dvs. en anonym funktion er en anonym lukning uden gratis variabler.
Kommentarer
- Dette jibes godt med lukkede og åbne formler i logik. Tak for dit svar.
- @RainDoctor: Gratis variabler defineres i logiske formler og i lambda-beregningsudtryk på en lignende måde: lambda i et lambda-udtryk fungerer som en kvantifier i logiske formler med gratis / bundne variabler .
Svar
En simpel forklaring i JavaScript:
var closure_example = function() { var closure = 0; // after first iteration the value will not be erased from the memory // because it is bound with the returned alertValue function. return { alertValue : function() { closure++; alert(closure); } }; }; closure_example();
alert(closure)
bruger den tidligere oprettede værdi af closure
. Den returnerede alertValue
-funktionens navneområde forbindes med det navneområde, hvor closure
-variablen ligger. Når du sletter hele funktionen, vises værdien af closure
-variablen slettes, men indtil da vil alertValue
-funktionen altid være i stand til at læse / skrive værdien af variablen closure
.
Hvis du kører denne kode, tildeler den første iteration en værdi 0 til closure
variablen og omskriv funktionen til:
var closure_example = function(){ alertValue : function(){ closure++; alert(closure); } }
Og fordi alertValue
har brug for den lokale variabel closure
for at udføre funktionen, binder den sig selv med værdien af den tidligere tildelte lokale variabel closure
.
Og nu hver gang du kalder closure_example
-funktion, den skriver den trinvise værdi af closure
-variablen, fordi alert(closure)
bundet.
closure_example.alertValue()//alerts value 1 closure_example.alertValue()//alerts value 2 closure_example.alertValue()//alerts value 3 //etc.
Kommentarer
- tak, jeg gjorde ikke ‘ t test koden =) alt synes at være i orden nu.
Svar
En “lukning” er , i det væsentlige, nogle lokale stater og nogle kode, kombineret til en pakke. Typisk kommer den lokale stat fra et omgivende (leksikalt) omfang, og koden er (i det væsentlige) en indre funktion, som derefter returneres til udvendigt. Lukningen er derefter en kombination af de fangede variabler, som den indre funktion ser, og koden for den indre funktion.
Det er en af de ting, der desværre er lidt svært at forklare på grund af være ukendt.
En analogi, som jeg med succes tidligere brugte, var “forestil os, at vi har noget, vi kalder” bogen “, i lukningen af rummet,” bogen “er den kopi der, over i hjørnet , af TAOCP, men på bordlukningen er det den kopi af en Dresden Files-bog. Så afhængigt af hvilken lukning du er i, resulterer koden “give mig bogen” i, at forskellige ting sker. “
Kommentarer
- Du har glemt dette: da.wikipedia.org/wiki/Closure_(computer_programming) i dit svar.
- Nej, jeg valgte bevidst ikke at lukke den side.
- ” Tilstand og funktion. “: Kan en C-funktion med en
static
lokal variabel betragtes som en lukning? Betegner lukninger i Haskell involverer tilstand? - @Giorgio Lukninger i Haskell lukker (tror jeg) over argumenterne i det leksikale omfang, de ‘ er defineret i, så jeg ‘ d siger ” ja ” (selvom jeg i bedste fald ikke er fortrolig med Haskell). AC-funktion med en statisk variabel er i bedste fald en meget begrænset lukning (du vil virkelig være i stand til at oprette flere lukninger fra en enkelt funktion med en
static
lokal variabel, du har nøjagtigt en). - Jeg stillede dette spørgsmål med vilje, fordi jeg tror, at en C-funktion med en statisk variabel ikke er en lukning: den statiske variabel er defineret lokalt og kun kendt inde i lukningen, den har ikke adgang miljøet. Jeg er heller ikke 100% sikker, men jeg vil formulere din erklæring omvendt: du bruger lukningsmekanismen til at oprette forskellige funktioner (en funktion er en lukningsdefinition + en binding til dens gratis variabler).
Svar
Det er svært at definere, hvad lukning er uden at definere begrebet “tilstand”.
Grundlæggende , på et sprog med fuld leksikalsk scoping, der behandler funktioner som førsteklasses værdier, sker der noget særligt. Hvis jeg skulle gøre noget som:
function foo(x) return x end x = foo
Variablen x
refererer ikke kun til function foo()
men det refererer også til tilstanden foo
var tilbage i sidste gang den vendte tilbage. Den virkelige magi sker, når foo
har andre funktioner, der er yderligere defineret inden for dets rækkevidde; det er som sit eget mini-miljø (ligesom “normalt” definerer vi funktioner i et globalt miljø).
Funktionelt kan det løse mange af de samme problemer som C ++ (C?) “s” statiske “nøgleord, som bevarer en lokal variabels tilstand gennem flere funktionsopkald; dog er det mere som at anvende det samme princip (statisk variabel) på en funktion, da funktioner er førsteklasses værdier; lukning tilføjer understøttelse af, at hele funktionens tilstand skal gemmes (intet at gøre med C ++ “s statiske funktioner).
Behandling af funktioner som førsteklasses værdier og tilføjelse af understøttelse af lukninger betyder også, at du kan have mere end en forekomst af den samme funktion i hukommelsen (svarende til klasser). Hvad dette betyder er, at du kan genbruge samme kode uden at skulle nulstille funktionens tilstand, som det kræves, når man beskæftiger sig med C ++ statiske variabler inde i en funktion (kan være forkert ved dette?).
Her er nogle test af Luas lukningsunderstøttelse .
--Closure testing --By Trae Barlow -- function myclosure() print(pvalue)--nil local pvalue = pvalue or 10 return function() pvalue = pvalue + 10 --20, 31, 42, 53(53 never printed) print(pvalue) pvalue = pvalue + 1 --21, 32, 43(pvalue state saved through multiple calls) return pvalue end end x = myclosure() --x now references anonymous function inside myclosure() x()--nil, 20 x() --21, 31 x() --32, 42 --43, 53 -- if we iterated x() again
resultater:
nil 20 31 42
Det kan blive vanskeligt, og det varierer sandsynligvis fra sprog til sprog, men det ser ud til i Lua, at når en funktion udføres, nulstilles dens tilstand. Jeg siger dette, fordi resultaterne fra koden ovenfor ville være forskellige, hvis vi havde adgang til funktion / tilstand direkte (i stedet for gennem den anonyme funktion den returnerer), da pvalue
ville blive nulstillet tilbage til 10; men hvis vi får adgang til myclosures tilstand gennem x (den anonyme funktion) kan du se, at pvalue
lever og har et godt sted i hukommelsen. Jeg formoder, at der er lidt mere til det, måske nogen kan bedre forklare arten af implementeringen.
PS: Jeg kender ikke en slikke af C ++ 11 (andet end hvad der er i tidligere versioner), så bemærk at dette ikke er en sammenligning mellem lukninger i C ++ 11 og Lua. Også alle “linier trukket” fra Lua til C ++ er ligheder som statiske variabler og lukninger er ikke 100% de samme; selvom de undertiden bruges til at løse lignende problemer.
Det, jeg ikke er sikker på, er i kodeeksemplet ovenfor, om den anonyme funktion eller den højere ordens funktion betragtes som lukningen?
Svar
En lukning er en funktion, der har tilknyttet tilstand:
I perl opretter du lukninger som denne:
#!/usr/bin/perl # This function creates a closure. sub getHelloPrint { # Bind state for the function we are returning. my ($first) = @_;a # The function returned will have access to the variable $first return sub { my ($second) = @_; print "$first $second\n"; }; } my $hw = getHelloPrint("Hello"); my $gw = getHelloPrint("Goodby"); &$hw("World"); // Print Hello World &$gw("World"); // PRint Goodby World
Hvis vi ser på den nye funktionalitet, der leveres med C ++.
Det giver dig også mulighed for at binde den aktuelle tilstand til objektet:
#include <string> #include <iostream> #include <functional> std::function<void(std::string const&)> getLambda(std::string const& first) { // Here we bind `first` to the function // The second parameter will be passed when we call the function return [first](std::string const& second) -> void { std::cout << first << " " << second << "\n"; }; } int main(int argc, char* argv[]) { auto hw = getLambda("Hello"); auto gw = getLambda("GoodBye"); hw("World"); gw("World"); }
Svar
Lad os betragte en simpel funktion:
function f1(x) { // ... something }
Denne funktion kaldes en topniveaufunktion, fordi den ikke er indlejret i nogen anden funktion. Hver JavaScript-funktion forbinder med sig selv en liste over objekter kaldet en ” Omfangskæde “. Denne omfangskæde er en ordnet liste over genstande E ach af disse objekter definerer nogle variabler.
I topniveaufunktioner består omfangskæden af et enkelt objekt, det globale objekt. For eksempel har funktionen f1
ovenfor en omfangskæde, der har et enkelt objekt, der definerer alle de globale variabler. (bemærk, at udtrykket “objekt” her ikke betyder JavaScript-objekt, det er bare et implementeringsdefineret objekt, der fungerer som en variabel container, hvor JavaScript kan “slå op” variabler.)
Når dette funktion påkaldes, JavaScript opretter noget, der kaldes et “Aktiveringsobjekt” , og placerer det øverst i omfangskæden. Dette objekt indeholder alle de lokale variabler (f.eks. x
her). Derfor har vi nu to objekter i omfangskæden: det første er aktiveringsobjektet, og under det er det globale objekt.
Bemærk meget nøje, at de to objekter placeres i omfangskæden på FORSKELLIGE tidspunkter. Det globale objekt placeres, når funktionen er defineret (dvs. når JavaScript parses til funktionen og oprettes funktionsobjektet), og aktiveringsobjekt kommer ind, når funktionen påberåbes.
Så vi ved det nu:
- Hver funktion har en omfangskæde tilknyttet
- Nårfunktionen er defineret (når funktionsobjektet oprettes), JavaScript gemmer en scope-kæde med den funktion
- For top-niveau-funktioner indeholder scope-kæden kun det globale objekt på funktionsdefinitionstidspunktet og tilføjer en ekstra aktiveringsobjekt øverst ved indkaldelsestid
Situationen bliver interessant, når vi beskæftiger os med indlejrede funktioner. Så lad os oprette en:
function f1(x) { function f2(y) { // ... something } }
Når f1
bliver defineret, får vi en omfangskæde til den, der indeholder kun det globale objekt.
Nu når f1
bliver kaldt, får omfangskæden for f1
aktiveringsobjektet. Dette aktiveringsobjekt indeholder variablen x
og variablen f2
som er en funktion. Og bemærk at f2
bliver ved at blive defineret.Derfor gemmer JavaScript på dette tidspunkt også en ny omfangskæde til f2
. Omfangskæden, der er gemt til denne indre funktion, er den aktuelle omfangskæde, der er i kraft. Det aktuelle omfang kæde er i virkeligheden den for f1
“s. Derfor er f2
” s kæde f1
“s nuværende omfangskæde – som indeholder aktiveringsobjektet for f1
og det globale objekt.
Når f2
kaldes, får det det til sit eget aktiveringsobjekt, der indeholder y
, føjet til sin scope-kæde, som allerede indeholder aktiveringsobjektet for f1
og det globale objekt.
Hvis der var en anden indlejret funktion defineret inden for f2
, dets omfangskæde vil indeholde tre objekter på definitionstidspunktet (2 aktiveringsobjekter med to ydre funktioner og det globale objekt) og 4 på indkaldelsestidspunktet.
Så, nu undersøger vi tand hvordan scope-kæde fungerer, men vi har ikke talt om lukninger endnu.
Kombinationen af et funktionsobjekt og et scope (et sæt variable bindinger) hvor funktionens variabler løses kaldes en lukning i datalogisk litteratur – JavaScript den endelige guide af David Flanagan
De fleste funktioner påberåbes ved hjælp af den samme omfangskæde, der var i kraft, da funktionen blev defineret, og det betyder ikke rigtig, at der er en lukning involveret. Lukninger bliver interessante, når de påberåbes under en anden rækkevidde end den, der var gældende, da de blev defineret. Dette sker mest, når et indlejret funktionsobjekt returneres fra den funktion, inden for hvilken det blev defineret.
Når funktionen vender tilbage, fjernes det aktiveringsobjekt fra omfangskæden. Hvis der ikke var nogen indlejrede funktioner, er der ikke flere referencer til aktiveringsobjektet, og det indsamles skrald. Hvis der var definerede indlejrede funktioner, har hver af disse funktioner en henvisning til omfangskæden, og denne rækkevidde henviser til aktiveringsobjektet.
Hvis disse indlejrede funktionsobjekter forblev dog inden for deres ydre funktion, så vil de selv blive indsamlet skrald sammen med det aktiveringsobjekt, de henviste til. Men hvis funktionen definerer en indlejret funktion og returnerer den eller gemmer den i en ejendom et eller andet sted, så vil der være en ekstern reference til den indlejrede funktion. Det indsamles ikke skrald, og aktiveringsobjektet, det refererer til, indsamles heller ikke skrald.
I vores ovenstående eksempel returnerer vi ikke f2
fra f1
, og derfor, når et opkald til f1
vender tilbage, fjernes dets aktiveringsobjekt fra dets rækkevidde og indsamlet affald. Men hvis vi havde noget som dette:
function f1(x) { function f2(y) { // ... something } return f2; }
Her vil den tilbagevendende f2
have en omfangskæde, der vil indeholder aktiveringsobjektet for f1
, og det vil derfor ikke blive indsamlet skrald. På dette tidspunkt, hvis vi kalder f2
, vil det være i stand til at få adgang til f1
“s variabel x
selvom vi “er ude af f1
.
Derfor kan vi se, at en funktion holder dens omfangskæde med sig og med omfangskæden kommer alle aktiveringsobjekterne af ydre funktioner. Dette er essensen af lukning. Vi siger, at funktioner i JavaScript er “leksikalt scoped” , hvilket betyder at de gemmer det omfang, der var aktivt, da de blev defineret i modsætning til det omfang, der var aktivt, da de blev kaldt.
Der er en række kraftfulde programmeringsteknikker, der involverer lukninger som tilnærmelse af private variabler , hændelsesdrevet programmering, delvis anvendelse osv.
Bemærk også, at alt dette gælder for alle de sprog, der understøtter lukning. PHP (5.3+), Python, Ruby osv.
Svar
En lukning er en kompilatoroptimering (aka syntaktisk sukker?). Nogle mennesker har også omtalt dette som Fattige mands objekt .
Se svaret af Eric Lippert : (uddrag nedenfor)
Compileren genererer kode som denne:
private class Locals { public int count; public void Anonymous() { this.count++; } } public Action Counter() { Locals locals = new Locals(); locals.count = 0; Action counter = new Action(locals.Anonymous); return counter; }
Fornuftigt?
Du bad også om sammenligninger. VB og JScript skaber begge lukninger stort set på samme måde.
Kommentarer
- Dette svar er en CW, fordi jeg ikke ‘ fortjener point for Eric ‘ s store svar.Opstem det, som du finder passende. HTH
- -1: Din forklaring er for rod i C #. Lukning bruges på mange sprog og er meget mere end syntaktisk sukker på disse sprog og omfatter både funktion og tilstand.
- Nej, en lukning er hverken bare en ” optimering af compiler ” eller syntaktisk sukker. -1