Blog webdeveloperski Patryk yarpo Jar

Funkcje anonimowe w JS

Autor wiadomości Styczeń 19, 2011

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):

function example1()
{
    alert("example 1");
}

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

example2();

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...

// kod z powyzszego listingu

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

example2();

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:

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

example2();

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

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

example2(); // 1

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.

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

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

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:

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

Innymi słowy operator `()' "uaktywnia" funkcję.

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

function example2()
{
    return (function() { // 1
        alert("example 1");
    })(); // 2
}
example2();
  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:

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

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.

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());

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 🙂

Komentarze (1) Trackbacks (0)

Leave a comment

 

Brak trackbacków.