{ Despre notiunea de closure - in JavaScript }

Despre notiunea de closure - in JavaScript



Pentru ca nu prea am gasit mai nimic in limba ramana despre subiect, iata:
Prin closure (in romana cel mei apropiat cuvant ar fi 'apropiere' :-) ) se intelege o functie interna altei functii care are acces la variabilele functiei externe care o inglobeaza .
Daca sunteti familiarizati cu notiunea de scope (daca nu, notiunea de scope defineste 'orizontul' de vizibilitate a unor variabile dintr-o anumita zona de cod JavaScript), atunci o closure (pare a fi de genul feminin :-) ) are trei nivele de vizibilitate:
- scope-up propriu definit de parantezele {}
- variabilele externe apartinatoare functiei care o inglobeaza
- variabilele globale
De asemenea funtia interna - closure are acces si la parametrii functiei externe care o inglobeaza, dar atentie accesul la acestia se face prin referinta !, nu prin valoare.
Exemplu simplu de closure:
function showName (firstName, lastName) {

​var nameIntro = "Your name is ";
    // this inner function has access to the outer function's variables, including the parameter​
​function makeFullName () {
        
​return nameIntro + firstName + " " + lastName;
    
}
​return makeFullName ();

}
​showName ("Michael", "Jackson"); // Your name is Michael Jackson

sau in jQuery
$(function() {
​
​var selections = []; 
$(".niners").click(function() { // this closure has access to the selections variable​
selections.push (this.prop("name")); // update the selections variable in the outer function's scope​
});
​
});
Pana acum nimc prea spectaculos. Dar, o functie closure poate sa aiba acces la variabilele functiei externe chiar dupa aceasta isi termina executia ! Asta se intampla pentru ca in JavaScript orizontul de vizibilitate a variabilelor (scop chain in English) se pastreaza exact ca in momentul in care functia a fost creata. Sau mai in Romana o functie in Javascript se 'naste' intr-un ecosistem care se pastreaza pana cand garbage collector-ul decide sa o elimine din memorie. Un exemplu care ilustreaza aceasta:
function celebrityName (firstName) {
    var nameIntro = "This celebrity is ";
    // this inner function has access to the outer function's variables, including the parameter​
   function lastName (theLastName) {
        return nameIntro + firstName + " " + theLastName;
    }
    return lastName;
}
​
​var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned.​
​
​// The closure (lastName) is called here after the outer function has returned above​
​// Yet, the closure still has access to the outer function's variables and parameter​
mjName ("Jackson"); // This celebrity is Michael Jackson

O functie closure poate stoca referinte la variabile externe, pe care le poate accesa in varii momente ale executiei codului JavaScript. Si devine interesanta atunci cand valoarea unei variabile externe se schimba inainte de apelul functiei closure. Iata un exemplu de cum poate fi implementata solutia unei veriabile private (apud Douglas Crockford):
function celebrityID () {
    var celebrityID = 999;
    // We are returning an object with some inner functions​
    // All the inner functions have access to the outer function's variables​
    return {
        getID: function ()  {
            // This inner function will return the UPDATED celebrityID variable​
            // It will return the current value of celebrityID, even after the changeTheID function changes it​
          return celebrityID;
        },
        setID: function (theNewID)  {
            // This inner function will change the outer function's variable anytime​
            celebrityID = theNewID;
        }
    }
​
}
​
​var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned.​
mjID.getID(); // 999​
mjID.setID(567); // Changes the outer function's variable​
mjID.getID(); // 567: It returns the updated celebrityId variable

Problema apare atunci cand variabila externa accesata prin referinta de functia closure este modificata, de exemplu intr-un ciclu for. De ex.
// This example is explained in detail below (just after this code box).​
​function celebrityIDCreator (theCelebrities) {
    var i;
    var uniqueID = 100;
    for (i = 0; i < theCelebrities.length; i++) {
      theCelebrities[i]["id"] = function ()  {
        return uniqueID + i;
      }
    }
    
    return theCelebrities;
}
​
​var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
​var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
​var stalloneID = createIdForActionCelebs [0];

console.log(stalloneID.id()); // 103
Intentia e ca vectorul returnat sa aiba valorile 100, 101, 102, dar rezultatul obtinut este 103, 103, 103 ! De ce? Pentru ca variabila externa "i" accesata din closure, la momentul accesarii are aceasta valoare ! Nu uitati ca in closure se stocheaza referinte si nu valori ! Pentru a obtine rezultatul scontat vom folosi un alt concept interesant JavaScript, si anume functiile auto-invocabile.
function celebrityIDCreator (theCelebrities) {
    var i;
    var uniqueID = 100;
    for (i = 0; i < theCelebrities.length; i++) {
        theCelebrities[i]["id"] = function (j)  { // the j parametric variable is the i passed in on invocation of this IIFE​
            return function () {
                return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array​
            } () // BY adding () at the end of this function, we are executing it immediately and returning just the value of uniqueID + j, instead of returning a function.​
        } (i); // immediately invoke the function passing the i variable as a parameter​
    }
​
    return theCelebrities;
}
​
​var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
​var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
​var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id); // 100​
​var cruiseID = createIdForActionCelebs [1];
console.log(cruiseID.id); // 101
In acest fel se stocheaza o referinta la o functie cu parametrul aferent 'frozen in time' , care ulterior poate fi aplelata in contextul anterior si care va produce rezultetul dorit, adica vectorul 100,101,102.

Articol inspirat de aici: Javascript is sexy
Si nu e ??