Vraag Hoe werken JavaScript-sluitingen?


Hoe zou je JavaScript-sluitingen uitleggen aan iemand met kennis van de concepten waaruit ze bestaan ​​(bijvoorbeeld functies, variabelen en dergelijke), maar worden sluitingen zelf niet begrepen?

ik heb gezien het Scheme-voorbeeld gegeven op Wikipedia, maar helaas hielp het niet.


7654


oorsprong


antwoorden:


JavaScript-sluitingen voor beginners

Ingezonden door Morris op di, 2006-02-21 10:19. Sinds de community bewerkt.

Sluitingen zijn geen magie

Deze pagina geeft uitleg over sluitingen zodat een programmeur ze kan begrijpen - met behulp van werkende JavaScript-code. Het is niet voor goeroes of functionele programmeurs.

Sluitingen zijn niet moeilijk om te begrijpen als het kernconcept eenmaal is gegroeid. Ze zijn echter onmogelijk te begrijpen door academische papers of academisch georiënteerde informatie over hen te lezen!

Dit artikel is bedoeld voor programmeurs met enige programmeerervaring in een reguliere taal en die de volgende JavaScript-functie kan lezen:

function sayHello(name) {
  var text = 'Hello ' + name;
  var say = function() { console.log(text); }
  say();
}
sayHello('Joe');

Een voorbeeld van een sluiting

Twee één zin samenvattingen:

  • Een sluiting is een manier om te ondersteunen eersteklas functies; het is een uitdrukking die kan refereren aan variabelen binnen de scope (wanneer deze voor het eerst werd gedeclareerd), aan een variabele wordt toegewezen, als een argument aan een functie wordt doorgegeven, of wordt teruggegeven als een functieresultaat.

  • Of een sluiting is een stackframe dat wordt toegewezen wanneer een functie de uitvoering start, en niet bevrijd nadat de functie is geretourneerd (alsof een 'stapelframe' op de heap is toegewezen in plaats van op de stapel!).

De volgende code retourneert een verwijzing naar een functie:

function sayHello2(name) {
  var text = 'Hello ' + name; // Local variable
  var say = function() { console.log(text); }
  return say;
}
var say2 = sayHello2('Bob');
say2(); // logs "Hello Bob"

De meeste JavaScript-programmeurs zullen begrijpen hoe een verwijzing naar een functie naar een variabele wordt geretourneerd (say2) in de bovenstaande code. Als je dat niet doet, moet je daarnaar kijken voordat je sluitingen leert. Een programmeur die C gebruikt, zou de functie beschouwen als het teruggeven van een aanwijzer naar een functie en dat de variabelen say en say2 waren elk een verwijzing naar een functie.

Er is een kritisch verschil tussen een C-aanwijzer naar een functie en een JavaScript-verwijzing naar een functie. In JavaScript kunt u een functie-referentievariabele zien als een verwijzing naar een functie ook als een verborgen verwijzing naar een sluiting.

De bovenstaande code heeft een sluiting omdat de anonieme functie function() { console.log(text); } wordt verklaard binnen een andere functie, sayHello2() in dit voorbeeld. In JavaScript, als u de function sleutelwoord binnen een andere functie, maakt u een afsluiting.

In C en de meeste andere veel voorkomende talen, na een functie retourneert, alle lokale variabelen zijn niet langer toegankelijk omdat het stapelframe is vernietigd.

Als u in JavaScript een functie binnen een andere functie declareert, kunnen de lokale variabelen toegankelijk blijven nadat ze zijn teruggekeerd uit de functie die u hebt gebeld. Dit is hierboven aangetoond, omdat we de functie noemen say2()nadat we terug zijn van sayHello2(). Merk op dat de code die we noemen verwijst naar de variabele text, wat een was lokale variabele van de functie sayHello2().

function() { console.log(text); } // Output of say2.toString();

Kijkend naar de output van say2.toString(), we kunnen zien dat de code verwijst naar de variabele text. De anonieme functie kan verwijzen text welke de waarde vasthoudt 'Hello Bob' omdat de lokale variabelen van sayHello2() worden in een afsluiting gehouden.

De magie is dat in JavaScript een functie-verwijzing ook een geheime referentie heeft naar de sluiting waarin deze is gemaakt - net als hoe deelnemers een methode-aanwijzer zijn plus een geheime verwijzing naar een object.

Meer voorbeelden

Om een ​​of andere reden lijken sluitingen heel moeilijk te begrijpen als je erover leest, maar als je enkele voorbeelden ziet, wordt duidelijk hoe ze werken (het heeft even geduurd). Ik raad aan de voorbeelden zorgvuldig door te nemen tot je begrijpt hoe ze werken. Als je sluitingen begint te gebruiken zonder dat je volledig begrijpt hoe ze werken, zou je snel een paar hele rare bugs maken!

Voorbeeld 3

Dit voorbeeld laat zien dat de lokale variabelen niet worden gekopieerd - ze worden bij referentie bewaard. Het is net zoiets als een stapelgeheugen in het geheugen te houden wanneer de buitenfunctie wordt afgesloten!

function say667() {
  // Local variable that ends up within closure
  var num = 42;
  var say = function() { console.log(num); }
  num++;
  return say;
}
var sayNumber = say667();
sayNumber(); // logs 43

Voorbeeld 4

Alle drie globale functies hebben een gemeenschappelijke verwijzing naar de dezelfde sluiting omdat ze allemaal binnen één oproep worden gedeclareerd setupSomeGlobals().

var gLogNumber, gIncreaseNumber, gSetNumber;
function setupSomeGlobals() {
  // Local variable that ends up within closure
  var num = 42;
  // Store some references to functions as global variables
  gLogNumber = function() { console.log(num); }
  gIncreaseNumber = function() { num++; }
  gSetNumber = function(x) { num = x; }
}

setupSomeGlobals();
gIncreaseNumber();
gLogNumber(); // 43
gSetNumber(5);
gLogNumber(); // 5

var oldLog = gLogNumber;

setupSomeGlobals();
gLogNumber(); // 42

oldLog() // 5

De drie functies hebben gedeelde toegang tot dezelfde sluiting - de lokale variabelen van setupSomeGlobals() toen de drie functies werden gedefinieerd.

Merk op dat in het bovenstaande voorbeeld, als u belt setupSomeGlobals() opnieuw, dan wordt een nieuwe sluiting (stack-frame!) gemaakt. De oude gLogNumber, gIncreaseNumber, gSetNumber variabelen worden overschreven met nieuwe functies die de nieuwe sluiting hebben. (In JavaScript, wanneer u een functie in een andere functie declareert, wordt / worden de interne functie (s) opnieuw gemaakt elk tijd waarop de externe functie wordt aangeroepen.)

Voorbeeld 5

Dit voorbeeld laat zien dat de afsluiting lokale variabelen bevat die zijn gedeclareerd in de buitenfunctie voordat deze werd afgesloten. Merk op dat de variabele alice wordt eigenlijk verklaard na de anonieme functie. De anonieme functie wordt als eerste gedeclareerd; en wanneer die functie wordt aangeroepen, kan deze toegang krijgen tot de alice variabele omdat alice is in dezelfde scope (JavaScript doet dit variabel hijsen). Ook sayAlice()() roept gewoon rechtstreeks de functie waarnaar gerefereerd wordt sayAlice() - het is precies hetzelfde als wat eerder werd gedaan maar zonder de tijdelijke variabele.

function sayAlice() {
    var say = function() { console.log(alice); }
    // Local variable that ends up within closure
    var alice = 'Hello Alice';
    return say;
}
sayAlice()();// logs "Hello Alice"

Tricky: merk ook op dat het say variabele is ook binnen de sluiting en kan worden geopend door een andere functie die binnen kan worden gedeclareerd sayAlice(), of het kan recursief binnen de interne functie worden geopend.

Voorbeeld 6

Deze is voor veel mensen een echte gotcha, dus je moet het begrijpen. Wees heel voorzichtig als u een functie binnen een lus definieert: de lokale variabelen van de sluiting werken mogelijk niet zoals u misschien eerst denkt.

U moet de functie "variabel hijsen" in Javascript begrijpen om dit voorbeeld te begrijpen.

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + i;
        result.push( function() {console.log(item + ' ' + list[i])} );
    }
    return result;
}

function testList() {
    var fnlist = buildList([1,2,3]);
    // Using j only to help prevent confusion -- could use i.
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

 testList() //logs "item2 undefined" 3 times

De lijn result.push( function() {console.log(item + ' ' + list[i])} voegt drie keer een verwijzing naar een anonieme functie toe aan de resultaatarray. Als u niet zo vertrouwd bent met anonieme functies, denk er dan over als:

pointer = function() {console.log(item + ' ' + list[i])};
result.push(pointer);

Merk op dat wanneer u het voorbeeld uitvoert, "item2 undefined" wordt driemaal gelogd! Dit komt omdat, net als voorgaande voorbeelden, er slechts één afsluiting voor de lokale variabelen is buildList (welke zijn result, i en item). Wanneer de anonieme functies op de regel worden aangeroepen fnlist[j](); ze gebruiken allemaal dezelfde enkele sluiting en gebruiken de huidige waarde voor i en item binnen die ene sluiting (waar i heeft de waarde van 3 omdat de lus voltooid was, en item heeft de waarde van 'item2'). Merk op dat we vanaf 0 dus indexeren item heeft de waarde van item2. En de i ++ neemt toe i naar de waarde 3.

Het kan handig zijn om te zien wat er gebeurt als een bloklevelige declaratie van de variabele item wordt gebruikt (via de let keyword) in plaats van een functie-afhankelijke variabele declaratie via de var trefwoord. Als die wijziging is aangebracht, wordt elke anonieme functie in de array gebruikt result heeft zijn eigen sluiting; wanneer het voorbeeld wordt uitgevoerd, is de uitvoer als volgt:

item0 undefined
item1 undefined
item2 undefined

Als de variabele i wordt ook gedefinieerd met let in plaats van var, dan is de uitvoer:

item0 1
item1 2
item2 3

Voorbeeld 7

In dit laatste voorbeeld maakt elke aanroep van de hoofdfunctie een afzonderlijke afsluiting.

function newClosure(someNum, someRef) {
    // Local variables that end up within closure
    var num = someNum;
    var anArray = [1,2,3];
    var ref = someRef;
    return function(x) {
        num += x;
        anArray.push(num);
        console.log('num: ' + num +
            '; anArray: ' + anArray.toString() +
            '; ref.someVar: ' + ref.someVar + ';');
      }
}
obj = {someVar: 4};
fn1 = newClosure(4, obj);
fn2 = newClosure(5, obj);
fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4;
fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4;
obj.someVar++;
fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;
fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;

Overzicht

Als alles volkomen onduidelijk lijkt, is het beste om met de voorbeelden te spelen. Een uitleg lezen is veel moeilijker dan het begrijpen van voorbeelden. Mijn uitleg over sluitingen en stapelframes, etc. zijn technisch niet correct - het zijn grove vereenvoudigingen bedoeld om te helpen begrijpen. Zodra het basisidee grokked is, kunt u later de details ophalen.

Laatste punten:

  • Wanneer je het gebruikt function binnen een andere functie wordt een sluiting gebruikt.
  • Wanneer je het gebruikt eval() binnen een functie wordt een sluiting gebruikt. De tekst jou eval kan verwijzen naar lokale variabelen van de functie en binnen eval u kunt zelfs nieuwe lokale variabelen maken met behulp van eval('var foo = …')
  • Wanneer je gebruikt new Function(…) (de Functie constructor) in een functie, wordt er geen sluiting gecreëerd. (De nieuwe functie kan niet verwijzen naar de lokale variabelen van de buitenfunctie.)
  • Een afsluiting in JavaScript is als het bijhouden van een kopie van alle lokale variabelen, net zoals ze waren toen een functie werd afgesloten.
  • Het is waarschijnlijk het beste om te denken dat een afsluiting altijd alleen een invoer voor een functie is en dat de lokale variabelen aan die sluiting worden toegevoegd.
  • Elke keer dat een functie met een afsluiting wordt aangeroepen, wordt er een nieuwe set lokale variabelen bijgehouden (aangezien de functie daarin een functieverklaring bevat en een verwijzing naar die interne functie wordt geretourneerd of wordt er op een of andere manier een externe verwijzing voor bewaard ).
  • Twee functies kunnen eruit zien alsof ze dezelfde brontekst hebben, maar hebben volledig ander gedrag vanwege hun 'verborgen' sluiting. Ik denk niet dat JavaScript-code kan achterhalen of een functie-verwijzing een afsluiting heeft of niet.
  • Als u probeert dynamische wijzigingen in de broncode aan te brengen (bijvoorbeeld: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola'));), het zal niet werken als myFunction is een afsluiting (je zou natuurlijk nooit denken aan vervanging van broncode-tekenreeks tijdens runtime, maar ...).
  • Het is mogelijk om functie-verklaringen binnen functieverklaringen binnen functies te krijgen - en u kunt sluitingen op meer dan één niveau krijgen.
  • Ik denk dat een sluiting gewoonlijk een term is voor zowel de functie als de variabelen die worden vastgelegd. Merk op dat ik die definitie in dit artikel niet gebruik!
  • Ik vermoed dat sluitingen in JavaScript afwijken van die welke normaal in functionele talen worden gevonden.

Links

Bedankt

Als je hebt net geleerde sluitingen (hier of elders!), dan ben ik geïnteresseerd in feedback van jou over eventuele wijzigingen die je zou kunnen doen om dit artikel duidelijker te maken. Stuur een e-mail naar morrisjohns.com (morris_closure @). Houd er rekening mee dat ik geen guru ben op JavaScript - noch op sluitingen.


Originele post door Morris is te vinden in de Internetarchief.


6160



Telkens wanneer u het functiezoekwoord binnen een andere functie ziet, heeft de interne functie toegang tot variabelen in de buitenfunctie.

function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp)); // will log 16
  }

  bar(10);
}

foo(2);

Dit logt altijd 16, omdat bar kan toegang krijgen tot de x die werd gedefinieerd als een argument voor foo, en het heeft ook toegang tmp van foo.

Dat is een sluiting. Een functie hoeft niet te terugkeer om een ​​afsluiting te worden genoemd. Gewoon toegang krijgen tot variabelen buiten je directe lexicale bereik creëert een afsluiting.

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // will also log 16
  }
}

var bar = foo(2); // bar is now a closure.
bar(10);

De functie hierboven logt ook 16, omdat bar kan nog steeds verwijzen naar x en tmp, hoewel het niet langer rechtstreeks binnen de scope valt.

Sindsdien echter tmp hangt nog steeds aan de binnenkant barsluiting, het wordt ook verhoogd. Telkens wanneer u belt, wordt deze verhoogd bar.

Het eenvoudigste voorbeeld van een afsluiting is dit:

var a = 10;
function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

Wanneer een JavaScript-functie wordt aangeroepen, wordt een nieuwe uitvoeringscontext gemaakt. Samen met de functieargumenten en het bovenliggende object ontvangt deze uitvoeringscontext ook alle variabelen die buiten de declaratie zijn gedeclareerd (in het bovenstaande voorbeeld, zowel 'a' als 'b').

Het is mogelijk om meer dan één afsluitfunctie te maken, door een lijst van deze te retourneren of door deze in te stellen op globale variabelen. Al deze zullen verwijzen naar de dezelfde  x en hetzelfde tmp, ze maken geen eigen kopieën.

Hier het nummer x is een letterlijk getal. Zoals met andere literals in JavaScript, wanneer foo wordt genoemd, het nummer x is gekopieerd in foo als zijn argument x.

Aan de andere kant gebruikt JavaScript altijd verwijzingen wanneer objecten worden gebruikt. Als je zegt, heb je gebeld foomet een object, zal de sluiting die het teruggeeft zal referentie dat originele object!

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    console.log(x.memb);
  }
}

var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

Zoals verwacht, elke oproep aan bar(10) zal toenemen x.memb. Wat misschien niet wordt verwacht, is dat x verwijst eenvoudigweg naar hetzelfde object als het age variabele! Na een paar telefoontjes naar bar, age.memb zal 2 zijn! Deze verwijzing vormt de basis voor geheugenlekken met HTML-objecten.


3804



VOORWOORD: dit antwoord is geschreven toen de vraag was:

Zoals de oude Albert zei: "Als je het niet kunt uitleggen aan een zesjarige, begrijp je het echt niet zelf." Nou, ik probeerde de JS-sluitingen uit te leggen aan een 27-jarige vriend en faalde volledig.

Kan iemand denken dat ik 6 ben en vreemd geïnteresseerd in dat onderwerp?

Ik ben er vrij zeker van dat ik een van de weinigen was die letterlijk de eerste vraag probeerde te beantwoorden. Sindsdien is de vraag meerdere keren gemuteerd, dus mijn antwoord lijkt nu ongelooflijk dwaas en misplaatst. Hopelijk blijft het algemene idee van het verhaal voor sommigen leuk.


Ik ben een grote fan van analogie en metaforen bij het uitleggen van moeilijke concepten, dus laat me mijn hand proberen met een verhaal.

Er was eens:

Er was een prinses ...

function princess() {

Ze leefde in een wondere wereld vol avonturen. Ze ontmoette haar Prince Charming, reed rond haar wereld op een eenhoorn, vecht draken, ontmoette sprekende dieren, en vele andere fantastische dingen.

    var adventures = [];

    function princeCharming() { /* ... */ }

    var unicorn = { /* ... */ },
        dragons = [ /* ... */ ],
        squirrel = "Hello!";

    /* ... */

Maar ze zou altijd terug moeten keren naar haar saaie wereld van klusjes en volwassenen.

    return {

En ze vertelde hen vaak over haar nieuwste geweldige avontuur als een prinses.

        story: function() {
            return adventures[adventures.length - 1];
        }
    };
}

Maar alles wat ze zouden zien is een klein meisje ...

var littleGirl = princess();

... verhalen vertellen over magie en fantasie.

littleGirl.story();

En hoewel de volwassenen echte prinsessen kenden, zouden ze nooit in de eenhoorns of draken geloven omdat ze ze nooit zouden kunnen zien. De volwassenen zeiden dat ze alleen in de verbeelding van het kleine meisje bestonden.

Maar we kennen de echte waarheid; dat het kleine meisje met de prinses erin ...

... is echt een prinses met een klein meisje erin.


2251



De vraag serieus nemen, moeten we uitzoeken wat een typische 6-jarige in staat is om cognitief, hoewel iemand die geïnteresseerd is in JavaScript is niet zo typisch.

Op Jeugdontwikkeling: 5 tot 7 jaar  het zegt:

Uw kind kan instructies in twee stappen volgen. Als u bijvoorbeeld tegen uw kind zegt: "Ga naar de keuken en pak een vuilniszak", dan kunnen ze die richting onthouden.

We kunnen dit voorbeeld als volgt gebruiken om sluitingen uit te leggen:

De keuken is een sluiting die een lokale variabele heeft, genaamd trashBags. Er is een functie in de keuken genaamd getTrashBag dat krijgt een vuilniszak en retourneert het.

We kunnen dit in JavaScript als volgt coderen:

function makeKitchen () {
  var trashBags = ['A', 'B', 'C']; // only 3 at first

  return {
    getTrashBag: function() {
      return trashBags.pop();
    }
  };
}

var kitchen = makeKitchen();

kitchen.getTrashBag(); // returns trash bag C
kitchen.getTrashBag(); // returns trash bag B
kitchen.getTrashBag(); // returns trash bag A

Andere punten die verklaren waarom sluitingen interessant zijn:

  • Elke keer makeKitchen() wordt genoemd, wordt een nieuwe afsluiting gemaakt met een eigen scheiding trashBags.
  • De trashBags variabele is lokaal aan de binnenkant van elke keuken en is niet toegankelijk buiten, maar de innerlijke functie op de getTrashBageigendom heeft er toegang toe.
  • Elke functie-aanroep zorgt voor een sluiting, maar er is geen noodzaak om de sluiting rond te houden, tenzij een innerlijke functie, die toegang heeft tot de binnenkant van de sluiting, van buiten de sluiting kan worden aangeroepen. Het object retourneren met de getTrashBag functie doet dat hier.

691



The Straw Man

Ik moet weten hoe vaak een knop is aangeklikt en iets doen bij elke derde klik ...

Vrij voor de hand liggende oplossing

// Declare counter outside event handler's scope
var counter = 0;
var element = document.getElementById('button');

element.addEventListener("click", function() {
  // Increment outside counter
  counter++;

  if (counter === 3) {
    // Do something every third time
    console.log("Third time's the charm!");

    // Reset counter
    counter = 0;
  }
});
<button id="button">Click Me!</button>

Dit zal nu werken, maar het dringt wel door in de buitenste scope door een variabele toe te voegen, waarvan het enige doel is om de telling bij te houden. In sommige situaties verdient dit de voorkeur omdat uw externe toepassing mogelijk toegang tot deze informatie nodig heeft. Maar in dit geval veranderen we alleen het gedrag van elke derde klik, dus het heeft de voorkeur om dit te doen sluit deze functionaliteit binnen de gebeurtenishandler in.

Overweeg deze optie

var element = document.getElementById('button');

element.addEventListener("click", (function() {
  // init the count to 0
  var count = 0;

  return function(e) { // <- This function becomes the click handler
    count++; //    and will retain access to the above `count`

    if (count === 3) {
      // Do something every third time
      console.log("Third time's the charm!");

      //Reset counter
      count = 0;
    }
  };
})());
<button id="button">Click Me!</button>

Let hier een paar dingen op.

In het bovenstaande voorbeeld gebruik ik het afsluitgedrag van JavaScript. Met dit gedrag kan elke functie onbeperkt toegang krijgen tot de scope waarin deze is gemaakt. Om dit praktisch toe te passen, roep ik onmiddellijk een functie aan die een andere functie retourneert, en omdat de functie die ik terugkrijg toegang heeft tot de interne tellingsvariabele (vanwege het hierboven toegelichte afsluitgedrag), resulteert dit in een privébereik voor gebruik door de resulterende functie ... niet zo eenvoudig? Laten we het afzwakken ...

Een eenvoudige sluiting met één regel

//          _______________________Immediately invoked______________________
//         |                                                                |
//         |        Scope retained for use      ___Returned as the____      |
//         |       only by returned function   |    value of func     |     |
//         |             |            |        |                      |     |
//         v             v            v        v                      v     v
var func = (function() { var a = 'val'; return function() { alert(a); }; })();

Alle variabelen buiten de geretourneerde functie zijn beschikbaar voor de geretourneerde functie, maar ze zijn niet direct beschikbaar voor het geretourneerde functieobject ...

func();  // Alerts "val"
func.a;  // Undefined

Snap je? In ons primaire voorbeeld bevindt de variabele count zich dus in de afsluiting en is deze altijd beschikbaar voor de gebeurtenishandler, dus deze behoudt zijn status van klik tot klik.

Ook is deze privévariabele status geheel toegankelijk, voor zowel metingen als toewijzen aan zijn private scoped variabelen.

Daar ga je; je bent nu volledig ingekapseld in dit gedrag.

Volledige blogpost (inclusief jQuery-overwegingen)


526



Afsluitingen zijn moeilijk uit te leggen omdat ze worden gebruikt om gedrag te laten werken waarvan iedereen intuïtief verwacht dat het toch werkt. Ik vind de beste manier om ze uit te leggen (en de manier waarop dat gebeurt) ik geleerd wat ze doen) is om de situatie zonder hen voor te stellen:

    var bind = function(x) {
        return function(y) { return x + y; };
    }
    
    var plus5 = bind(5);
    console.log(plus5(3));

Wat zou hier gebeuren als JavaScript niet weet je sluitingen? Vervang gewoon de aanroep in de laatste regel door zijn methode-instantie (wat in feite is wat functie-aanroepen doen) en u krijgt:

console.log(x + 3);

Nu, waar is de definitie van x? We hebben het niet gedefinieerd in de huidige scope. De enige oplossing is om te laten plus5  dragen de reikwijdte (of eigenlijk het bereik van de ouder) rond. Op deze manier x is goed gedefinieerd en is gebonden aan de waarde 5.


444



Dit is een poging om verschillende (mogelijke) misverstanden over sluitingen op te lossen die in sommige van de andere antwoorden voorkomen.

  • Een afsluiting wordt niet alleen gecreëerd wanneer u een innerlijke functie retourneert. In feite is de insluitende functie hoeft helemaal niet terug te komen om de sluiting ervan te creëren. U zou in plaats daarvan uw innerlijke functie kunnen toewijzen aan een variabele in een buitenste bereik, of deze als een argument doorgeven aan een andere functie waar deze onmiddellijk of later zou kunnen worden aangeroepen. Daarom is waarschijnlijk de sluiting van de insluitfunctie gemaakt zodra de insluitende functie wordt aangeroepen omdat elke innerlijke functie toegang heeft tot die sluiting wanneer de innerlijke functie wordt aangeroepen, vóór of nadat de insluitende functie terugkeert.
  • Een afsluiting verwijst niet naar een kopie van de oude waarden van variabelen binnen het toepassingsgebied. De variabelen zelf maken deel uit van de afsluiting, en dus is de waarde die wordt gezien bij het openen van een van die variabelen de laatste waarde op het moment dat deze wordt benaderd. Dit is de reden waarom binnenfuncties die binnen in loops zijn gemaakt, lastig kunnen zijn, omdat elke functie toegang heeft tot dezelfde buitenvariabelen in plaats van een kopie van de variabelen te pakken op het moment dat de functie wordt gemaakt of wordt opgeroepen.
  • De "variabelen" in een afsluiting omvatten alle genoemde functies verklaard binnen de functie. Ze bevatten ook argumenten voor de functie. Een afsluiting heeft ook toegang tot de variabelen van de bevattende sluiting, helemaal tot aan de wereldwijde reikwijdte.
  • Sluitingen gebruiken geheugen, maar ze veroorzaken geen geheugenlekken omdat JavaScript zelf zijn circulaire structuren opruimt waarnaar niet wordt verwezen. Internet Explorer-geheugenlekken met sluitingen worden gemaakt als het DOM-kenmerkwaarden die verwijzen naar sluitingen niet loskoppelt, waardoor verwijzingen naar mogelijk circulaire structuren worden behouden.

342