Framework-uri JavaScript: Programare functionala in MooTools


Am amintit cand am vorbit prima data de JavaScript, de notiunea de inchidere. Aceasta notiune este esentiala in programarea functionala; cu toate ca nu am spus-o explicit atunci, dar JavaScript este un limbaj de programare functionala. MooTools face si mai usor folosirea conceptelor din programarea functionala, prin extinderea clasei native Function. Cei care nu au cunostinte de programare functionala s-ar putea sa nu inteleaga mare lucru din explicatiile de mai jos, asa ca, deocamdata, pot trece linistiti peste ele. Ne sunt puse astfel la dispozitie urmatoarele metode:

  • Function.pass primeste doi parametri: primul parametru poate fi un obiect sau un vector de obiecte, care sunt transmise ca argumente functiei, fara ca aceasta sa fie apelata, si un al doilea parametru, optional, care reprezinta un obiect la care este legata instanta lui this din interiorul functiei (adica la fiecare folosire a lui this in interiorul functiei, this va fi o referinta la acest al doilea parametru – pentru mai multe detalii vezi articolul Obiecte in JavaScript, discutia despre Function.call). Rezultatul apelului acestei metode este o functie, care poate fi apelata mai tarziu in cod, fara argumente. Foarte important, daca functia initiala astepta ca parametru un vector, il putem pasa printr-un apel de genul: functie.pass([[1, 2, 3, 4]]); (deci transmitem ca parametru un vector cu un singur element, acest singur element avand tipul Array). Daca vi se pare confuz, haideti sa incercam un exemplu:
var plus = function(a, b) {
    return a+b;
};
alert(plus(2,3)); // 5
var cinci = plus.pass([2,3]);
// observati ca trebuiau transmisi doi parametri,
// deci i-am pus intr-un vector
// cinci este o *functie*, diferita de plus,
// care are deja cei doi parametri stocati intern
// $type(variabila) intoarce tipul unei variabile
alert($type(cinci)); // function
alert(cinci()); // 5
alert($type(cinci())); // number

Din exemplul de mai sus este posibil sa observati si de ce atunci cand functia primeste un singur parametru pe care il asteapta ca fiind vector, e nevoie sa punem acel vector intr-un alt vector care contine un singur element: daca am pasa ca parametru [1,2], functiei i-ar fi trimisi 2 parametri, primul cu valoarea 1 si al doilea cu valoarea 2. Daca in schimb trimitem [[1,2]] functiei ii sunt trimise toate elementele din vector ca parametri, astfel ca functiei ii este trimis un singur parametru – pentru ca este vorba de un vector cu un singur element – si anume vectorul [1,2]

  • Function.attempt primeste aceiasi parametri ca si Function.call, dar de data aceasta functia este si apelata. Daca in mod normal functia arunca o exceptie, Function.attempt intoarce null si nu arunca exceptiile; altfel, este intors rezultatul apelului functiei. Astfel, daca folosim aceeasi functie plus de mai sus ca exemplu, plus() ar arunca o exceptie. Sa incercam un exemplu:
function first(v) {
    return v[0];
};
alert(first([1])); // 1
alert(first()); // eroare!
alert(first.attempt([[1]])); // 1
// din nou, first asteapta ca unic parametru un vector
// (vezi mai sus de ce sunt folosite doua paranteze drepte)
alert(first.attempt()); // null
  • Function.bind leaga (eng. binds) referinta lui this din codul unei functii, la primul parametru transmis. Al doilea parametru este acelasi cu primul parametru transmis lui Function.pass si Function.attempt, si este optional. Cu toate ca poate parea ca Function.bind este doar un alias pentru Function.call, Function.bind, la fel ca Function.pass, intoarce o noua functie, nu rezultatul apelului functiei. Exemplu:
var x='global';
var variabila = {
    x: 'variabila'
};
function spune() {
    alert('x: '+this.x);
};
spune(); // this == window, context global -> x: global
spune.call(variabila); // this == variabila -> x: variabila
var spune_legat = spune.bind(variabila);
alert($type(spune_legat)); // function
spune_legat(); // x: variabila

Daca o sa va uitati pe codul din cartea MooTools Essentials, scrisa de Aaron Newton (singura carte despre MooTools pana in acest moment, si care chiar merita citita), o sa vedeti ca in foarte multe exemple este folosit Function.bind. Motivul este acela ca IE6, al doilea cel mai folosit browser (din pacate), are o problema cu inchiderile (eng. closures – vezi Obiecte in JavaScript) care cauzeaza scurgeri de memorie (eng. memory leaks). E adevarat ca a aparut la un moment dat si un update, insa nu avem garantia ca toti utilizatorii unui site au toate update-urile la zi (daca ar fi atat de constiinciosi, nu ar folosi IE6 in primul rand), deci daca avem de-a face cu multe inchideri, este mai bine sa folosim Function.bind. (Cu toate ca inchiderile sunt o caracteristica foarte folositoare a JavaScript, si oricum nu putem folosi intotdeauna Function.bind; totusi, atunci cand putem, este recomandat sa il folosim.)

  • Function.bindWithEvent este identica cu Function.bind, doar ca primul parametru care va fi transmis functiei, inainte de orice alt parametru transmis de noi, va fi o instanta Event care a declansat un eveniment, iar Function.bindWithEvent este folosita, evident, atunci cand lucram cu evenimente. Instanta de Event transmisa este valabila si pentru IE, si este normalizata in asa fel ca putem folosi acelasi cod pentru toate browserele (deci am scapat de una din diferentele intre browsere, pana acum)
  • Function.delay si Function.periodical primesc trei parametri, dintre care numai primul este obligatoriu: un interval de timp t (in ms), si cei doi parametri de care am mai vorbit la Function.pass. Efectul acestora este ca vor apela functia cu o intarziere de t ms/ periodic la fiecare t ms. Acestea sunt echivalente cu setTimeout/setPeriodical, cele doua functii native ale JavaScript, numai ca este mai OO ca acestea sa fie metode ale lui Function, decat sa fie functii globale. Valoarea returnata de cele doua este un intreg, care poate fi folosit de functia (globala) $clear (in JavaScript, $ este un caracter valid pentru nume de variabile) pentru a opri apelarea intarziata/periodica. $clear este echivalent cu ambele functii native clearTimeout si clearPeriodical. Exemplu:
var n = 0;
function enervant() {
    if (n>=5) {
        $clear(enervant_periodic);
        // acesta este unul din rarele cazuri
        // in care nu putem folosi inchideri
        // sau cel putin, este mai simplu decat
        // sa incercam artificii pentru Function.bind
        return;
    }
    n++;
    alert('Enervant, nu-i asa?');
};
var enervant_periodic = enervant.periodical(500);
  • Function.run functioneaza la fel ca Function.attempt, doar ca exceptiile sunt aruncate.
  • in fine, Function.create creaza o copie a unei functii. Primeste un singur parametru – un Object care reprezinta optiuni, si este folositor atunci cand e nevoie sa folosim mai multe dintre metodele lui Function deodata (de exemplu, vrem sa intarziem executia si sa folosim functia impreuna cu evenimente). Lista de parametri o gasiti in documentatia oficiala, si din tot ce am discutat pana acum ar trebui sa fi suficient de usor de inteles.

Totusi, exista un concept din programarea functionala – functii curry – care nu este implementat de MooTools. Un articol interesant in care autorul implementeaza o metoda Function.curry in MooTools este How about a nice, spicy curry? Din nou, acest articol este destinat celor familiari cu programarea functionala; oricum, daca cineva nu tine mortis sa foloseasca function currying, poate fi intotdeauna evitat.

Data viitoare, vom vorbi despre manipularea DOM folosind MooTools.

, , , ,

  • http://www.dlpetcu.ro Dl. Petcu

    foarte bun micul tutorial