Funkcje anonimowe w JS

JS jako jeden z najbardziej ekspresyjnych języków posiada ciekawy i często wygodny mechanizm funkcji anonimowych.

Polega to na tworzeniu funkcji, które nie mają nazwy i są jakby przypisane do konkretnego miejsca w kodzie.

Spójrzmy na przykład (wykorzystanie zwykłych funkcji):

[javascript]function example1()
{
    alert("example 1");
}

function example2()
{
    // tu cos robimy
    return example1();
}

example2();[/javascript]

Załóżmy teraz, że funkcja `example1′ jest wywoływana tylko w jednym miejscu, co więcej jej funkcjonalność jest ściśle związana z `example2′.

W tym wypadku jest jednak możliwość zrobienia sobie psikusa…

[javascript]// kod z powyzszego listingu

// do zmiennej example1 przypisujemy
// referencje na inna funkcje!
example1 = parseInt; 

example2();[/javascript]

I co taki kod zrobi? Zwróci `NaN‚.

Dlaczego? Bo `example1’ wskazywało przy drugim wywołaniu na funkcję `parseInt‚, która przyjmuje jeden parametr i próbuje sparsować go do wartości liczby całkowitej. Skoro `example1’  otrzymywało ciąg „example1”, to nie da się z tego zrobić liczby. Stąd wartość Not a Number – NaN.

Jak to poprawić?

Funkcje wewnętrzne

W Javascript nie istnieją przestrzenie nazw, a przynajmniej nie na zasadzie słowa kluczowego „namespace”. Można jednak osiągnąć bardzo podobny efekt za pomocą wykorzystania mechanizmu domknięć. Więc przeczytasz tu:

Możemy zatem zrobić coś takiego:

[javascript]function example2()
{
    function example1()
    {
        alert("example 1");
    }
    // tu cos robimy
    return example1();
}

example2();[/javascript]

W ten sposób nie ma możliwości nadpisania funkcji `example1′, możemy być zatem pewnie wyniku działania `example2′. Chyba, że ktoś nam nadpisze `example1′ 😉

Gdybyśmy jednak chcieli uprościć zapis, możemy to zrobić w sposób pokazany poniżej.

Funkcje anonimowe

[javascript]function example2()
{
    // tu cos robimy
    return function() { //2
        alert("example 1"); // 3
    }
}

example2(); // 1[/javascript]

Cóż to spowoduje? Nic. Dlaczego?! (Nic, to nie jest najlepsze określenie. Coś się stanie, jednak nie zobaczysz niczego).

Przypatrzmy się, co tu się dzieje:

  1. wywołujemy funkcję example2()
  2. funkcja ta zwraca funkcję. Nie wynik działania funkcji, a funkcję. Czyli, jak zrobimy tak:

Operator wywołania funkcji `()’

Z pewnością znasz wiele operatorów, choćby operator przypisania `=’, porównania `==’ lub `===’ (w JS istnieją dwa operatory porównujące). Także `()’ jest operatorem. Powoduje on próbę wykonania pewnego kodu, na który wskazuje zmienna.

[javascript]function example2()
{
    // tu cos robimy
    return function() { //2
        alert("example 1"); // 3
    }
}

var e = example2(); // 1
e();
e();
alert(e); // wyswietli kod[/javascript]

to uzyskamy dwukrotnie alert „example 1” oraz raz zobaczymy kod funkcji anonimowej. Co zrobiliśmy? Przypisaliśmy do zmiennej `e’ referencję na funkcję (teraz ta zmienna ma w sobie wartość adresu, gdzie ta funkcja się znajduje w pamięci. Jeśli wywołasz funkcję – przez dodanie `()’ za nazwą zmiennej `e’ – zostanie wywołana funkcja przypisana pod zmienną `e’).

W poprzednim przypadku (tym z funkcją jawnie zdefiniowaną i nazwaną example1), moglibyśmy to otrzymać tak:

[javascript]function example2()
{
    // tu cos robimy
    return example1; // zwracamy referencję na tę funkcję, nie wynik jej działania
}[/javascript]

Innymi słowy operator `()’ „uaktywnia” funkcję.

Aby od razu działało jak przykład 1:

[javascript]function example2()
{
    return (function() { // 1
        alert("example 1");
    })(); // 2
}
example2();[/javascript]
  1. całe ciało anonimowej funkcji objęliśmy w nawiasy.
  2. dodaliśmy za ciałem funkcji operator wywołania `()’.

Wynik powyższego kodu będzie identyczny do działania pierwszego listingu z tego artykułu.
To oczywiście nie wszystko na temat funkcji anonimowych. Ale o innych rzeczach (rekurencja, sprytne triki) napiszę kiedy indziej 🙂

Nazwana funkcja anonimowa

Funkcja anonimowa może mieć swoją nazwę! No to zaraz, ktoś powie: „Co to za anonimowa?”. No może mieć nazwę, ale widoczną tylko dla niej samej:

[javascript]function example2()
{
    return (function nazwaFunkcjiAnonimowej() { // 1
        alert(nazwaFunkcjiAnonimowej);
    })(); // 2
}
example2();[/javascript]

I po co? I co to daje? No można np. za pomocą funkcji anonimowej korzystać z rekurencji. Oto przykład najpopularniejszej rekurencji na świecie: silnia.

[javascript]function policzSilnieInterfejs()
{
    var MAX = 8,
        n;

    do
    {
        n = parseInt(prompt("Podaj liczbę <0; "+ MAX +">"));
    } while(n < 0 || n > MAX || isNaN(n));

    return (function factorial(n) {
        return (0 === n) ? 1 : (n * factorial(n - 1));
    })(n);
}
alert(policzSilnieInterfejs());[/javascript]

Można byłoby wykorzystać tu także obiekt `arguments’ i jego właściwość `callee’, ale  tym w osobnym wpisie:

  • obiekt arguments – TODO

Do zrozumienia powyższego kodu przydatne mogą się okazać artykuły:

Warto pobawić się samemu. Wujek google też z pewnością pomoże 🙂

Jedna odpowiedź do “Funkcje anonimowe w JS”

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *