onsdag 7 augusti 2013

Closure i JavaScript: Svårt att greppa, omöjligt att leva utan

Senaste tiden har jag fått nöjet att arbeta med JavaScript mycket. Och jag älskar JavaScript!

Det är ett mycket missförstått språk, men det är enormt kraftfullt. Men tyvärr har det blivit misshandlat på vägen och fått ett onödigt dåligt rykte. Men idag är det verkligen ett mycket kraftfullt språk som ingen kan leva utan.

Något av det svåraste att greppa med JavaScript må vara det som kallas "closure". Men det är också en av de kraftfullaste aspekterna med språket.

Låt oss titta på två exempel för att förklara vad det är, och jag ska försöka vara så tydlig och enkel som möjligt. Detta tog mig lång tid att fatta.

Exempel 1:

var digit_name = (function () {
  var names = ['zero', 'one', 'two', 'three', 'four', 'five'];

  return function (n) {
    return names[n];
  }
})();

console.log( digit_name(3) ); //Output: three


Så vad är det vi gör?
Först skapar vi en funktion, digit_name, som är en funktion som exekveras direkt och körs och retunerar namnet på siffran vi ger den som parameter.

Kör vi digit_name(2) så retunerar funktionen two. Det är ingen komplicerad funktion i sig, men den demostrerar "closure" välldigt bra.

Hurså?
Jo, det luddiga begreppet closure innebär att return-funktionen inut i vår huvudfunktion digit_name fortfarande har tillgång till alla variabler vi definierat även efter att huvudfunktionen returnerat, alltså avslutats.

I exemplet ovan så har vi en funktion, digit_name, som körs och returnerar när den skapas. Vår return retunerar ännu en funktion som i sin tur retunerar ett index i vår array names. Och det är tack vare closure så har vi ändå tillgång till names även i vår return funktion.

Låt oss kolla på ett annat exempel där vi ser hur closure fungerar.

Exempel 2:

function doSteps(){

  var level = 0;

  function step(){
    console.log('Step: ' + digit_name(level) );
    level++;
    if(level < 5){
      setTimeout( step, 1000 );
    }
  }

  setTimeout( step, 1000 );
}
doSteps();


Här har vi en funktion doSteps() som med hjälp av ett timing event, setTimeout, anropar funktionen step() efter 1 sekund. Vår funktion doSteps() har sedan länge då retunerat och avslutats. Men tack vare closure i JavaScript så har vi forfarande tillgång till variablen level.

I exemplet ovan använder vi även funktionen digit_name() från första exemplet, om det är någon som blev förvirrad. :-)

Fortfarande inte säker på saken? Kolla in videon nedan där fenomenala Douglas Crockford förklarar.