Pod lupą: lambda, funkcje anonimowe i domknięcia w JavaScript.

Power JavaScript

Poziom geek

Witam i zapraszam do dzisiejszego artykułu o zaawansowanych aspektach języka JavaScript. A właściwie bierzemy dokładnie pod lupę elementy języka, które są przez nas dość często używane podczas programowania, a jednak mogą być nie do końca lub niepoprawnie rozumiane. Tymczasem w nich tkwi moc!

Funkcje anonimowe i domknięcia w JavaScript? Jaka znowu lambda?

Można by powiedzieć „po co mi to wszystko, przecież wiem jak się tego używa”. W końcu zagłębianie się w to zabiera cenny czas. Oczywiście że tak, ale rozumienie i używanie tych elementów języka świadomie w swoich kodach nieco różni się od podpatrywania danych technik w gotowych już kodach.

Czasem jednak ktoś będąc w „geek mode” zaczyna bliżej zastanawiać się nad takimi tematami. Sam swego czasu czułem taką potrzebę, stąd też niniejszy artykuł, jako próba zebrania i podsumowania informacji w jednym miejscu.

O funkcjach jako takich, pisaliśmy już w poprzednich artykułach. Dziś po prostu wypływamy na głębsze wody.

Funkcje anonimowe w JavaScript

Zacznijmy od funkcji anonimowych. W języku JavaScript funkcje są niezmiernie ważnym elementem budulcowym. Konstrukcje te znamy i intensywnie ich używamy. Czy to jako zwykłe funkcje (nazwane), czy też właśnie jako funkcje anonimowe.

Różnica jest bardzo łatwa do zauważenia.

Definicja funkcji nazwanej:


function nazwa_naszej_funkcji() {
  // ... kod 
}

Natomiast funkcji anonimowej:


var nazwa_zmiennej = function() {
  // ... kod funkcji anonimowej
};

Mówiąc najprościej, funkcja anonimowa to taka funkcja, która została zadeklarowana bez żadnego nazwanego identyfikatora, poprzez który można ją wywołać.

Jak pokazuje przykład powyżej, funkcja anonimowa została zdefiniowana i przypisana do zmiennej. To jedno z zastosowań i w taki sposób możemy potrzebować odwołać się do funkcji anonimowej.

Ciekawszą możliwością jest napisanie funkcji zwracającej funkcję.

Oto przykład:


function myFuncGenerator() {
    // zwracanie kodu funkcji anonimowej

    return function() {
        alert("Tada!");
    }
}

// myFuncGenerator();
alert(myFuncGenerator());

Tym, na co należy zwrócić uwagę jest fakt, że funkcja myFuncGenerator zwraca nie wynik działania funkcji anonimowej, tylko samą funkcję. A konkretnie jej kod, dlatego gdy napiszemy wywołanie w ten sposób:

myFuncGenerator();

nie zobaczymy nic, choć w tym przypadku być może spodziewalibyśmy się alertu z napisem.

Należy posłużyć się operatorem wywołania funkcji, czyli: ‚()’ ale na samej funkcji anonimowej. Operator ten jest ściśle związany w funkcjami; jego użycie wykonuje próbę wykonania kodu, na który wskazuje zmienna, przy której operator ten został postawiony.

W związku z tym, aby dotrzeć do wyniku działania funkcji anonimowej, musimy napisać:


// ...

var my_test = myFuncGenerator();

my_test();
alert(my_test);

Główna funkcja zwraca funkcję anonimową, która to została przypisana do zmiennej. Następnie tę zmienną potraktowaliśmy operatorem wywołania funkcji, dzięki czemu zobaczyliśmy wynik działania funkcji anonimowej.

Co ciekawe sam kod funkcji anonimowej możemy zobaczyć wyświetlając samą zmienną:

alert(my_test);

Ten sam efekt da również wywołanie alert(myFuncGenerator()).

Najpopularniejszym zastosowaniem funkcji anonimowych jest użycie ich jako argumentów innych funkcji, lub jako domknięć (o tym za chwilę).

Przykłady użycia:


setTimeout(function() {
    alert('Test');
}, 500);

Nasza funkcja anonimowa zostaje przekazana do funkcji setTimeout – jej kod zostanie wykonany z opóźnieniem 500 ms.

Kolejny przykład to podnoszenie liczby do kwadratu:

var square = function(num) {return num * num};
alert(square(5)); // 25

Z kolei trzeci przykład:

var display = function(elem) {
    document.write(elem, ' | ');
};
[5, 4, 3, 2, 1].forEach(display);

to funkcja anonimowa, która zajmuje się wypisywaniem na stronie przekazanego argumentu oraz separatora. Zostaje przypisana do zmiennej i użyta jako argument metody forEach, iterująca po elementach tablicy liczb. Każdy element zostanie wypisany.

Bardzo eleganckie rozwiązanie.


Nazwane funkcje anonimowe

Być może brzmi to nieco dziwnie, ale jak na JavaScript przystało, i w tym obszarze znajdziemy coś ekscentrycznego.

Ale to nic strasznego. Po prostu funkcja anonimowa może mieć swoją nazwę, ale nazwa ta jest dostępna tylko dla niej samej. Dla nas to nadal funkcja anonimowa.

function anonymousFreak() {
    return (function Jadwiga() {
        alert(Jadwiga);
    })(); // !
}

anonymousFreak();

Jak widać nazwaliśmy funkcję anonimową i wywołaliśmy ją. W przykładzie tym zostanie wyświetlony kod funkcji Jadwiga.

To taka ciekawostka, ale można tę cechę zastosować na przykład do implementacji rekurencji.

Czym zatem jest lambda w JavaScript?

Za chwilę będzie jasne, a odpowiedź jest bardzo prosta.

Może przez chwilę rzućmy okiem na język Python. Ucząc się go dość szybko spotkamy się z wyrażeniami lambda:

lambda [arg1 [,arg2,…..argn]]:expression

Przykład:

g = lambda x: x*2
g(3)
# 6

Wygląda znajomo? Została stworzona funkcja anonimowa, który mnoży podany parametr przez 2.

W języku Python (czy też np. Ruby) istnieje specjalnie słowo kluczowe lambda, które pozwala wygodnie tworzyć funkcje anonimowe.

Czy zatem ta cała lambda w JavaScript to takie samo ustrojstwo jak w Pythonie?

Tak! W JavaScript nie mamy dedykowanego słowa kluczowego, tylko określoną składnię, więc lambda występuje tutaj umownie (bardziej intuicyjne dla programistów). Nie trzeba nawet posługiwać się tym terminem, ale możemy się z nim często spotkać w publikacjach, kodach czy dyskusjach, więc dobrze jest nie mylić go z niczym innym.

Lambda oznacza konstrukcje językowe pozwalające tworzyć funkcje anonimowe. Zdarza się, że są mylone z domknięciami, którymi się teraz zajmiemy.

Domknięcia w JavaScript

Przede wszystkim czym są domknięcia (ang. closures). Domknięcia znane są najlepiej w językach funkcyjnych, w których to funkcje mogą zwracać inne funkcje, wykorzystujące zmienne utworzone lokalnie.

Mają zatem także związek z zasięgiem zmiennych i właśnie przy rozważaniach tego tematu, również można się z domknięciami spotkać.

Mówiąc najprościej, domknięcie w językach programowania jest to obiekt wiążący funkcję i środowisko, w którym funkcja pracuje. Środowisko to przechowuje wszystkie obiekty używane przez funkcję i co ważne są one niedostępne w globalnym zakresie widoczności.

Spójrzmy na przykłady korzystające z domknięć.

Przykład 1:


function dodawanie_do(a) {
    return function(b) {
        return a + b;
    };
}

var suma_test = dodawanie_do(5);
alert(suma_test(10)); // 15

To jeden z najprostszych przykładów ilustrujących działanie domknięć w JavaScript.

Funkcja suma_test oprócz swojego argumentu formalnego (b), poprzez domknięcie posiada także dostęp do kopii wartości przekazywanej w funkcji nadrzędnej (dodawanie_do).

I to jest właśnie klucz do zrozumienia od podstaw działania domknięć.

Przykład 2 – dynamiczna zawartość elementu z użyciem jQuery i domknięcia:

var viewFactory = function viewFactory(elem) {
    return function set(html) {
        $(elem).html(html);
    };
};

Na tej samej zasadzie jak poprzednio, funkcje mają odpowiednio dostęp (poprzez domknięcie) do zmiennych (element oraz kod html, który zostanie do niego dodany).

Spójrzmy na jeszcze inny przykład. Pracując z framework’ami JavaScript, często możemy napotkać takie „opakowanie” dla kodu wykonującego jakieś zadania:


(function() {
    alert('Test');
}());

W uproszczeniu jest to podstawa „izolowania” kodu tak, aby nie występowały konflikty w innych jego częściach. W taki sposób podnosi się bezpieczeństwo kodu, ponieważ maleje ryzyko przypadkowego nadpisania wartości, ponieważ odizolowaliśmy go (i jego zmienne) od przestrzeni globalnej.

Sprawdza się to dobrze w praktyce, szczególnie tam gdzie nabiera największej wagi, czyli w przypadkach dołączania wielu różnych bibliotek (framework’ów) do projektu.

Przykładowo:

(function($) {

  // $('#id').hide(); // ...

})(jQuery);

Wiele bibliotek używa nazwy $ w celu trzymania referencji do funkcji frameworka (takie jak operacje na elementach html). I wielu programistów do tej nazwy przywykło. W nowszych wersjach jQuery stosowana jest właśnie nazwa ‚jQuery’.

Stosując „opakowanie” jak w przykładzie powyżej, mamy pewność że w jego wnętrzu (czyli w tym zasięgu) pod zmienną o nazwie $ zawsze będziemy mieli jQuery, a nie jakieś przypadkowe funkcjonalności z innych bibliotek projektu.

Dobrze, ale czym właściwie jest taki zapis kodu? Jest to po prostu (często spotykana) metoda używania funkcji anonimowej jako domknięcia.

Podsumowanie

I na tym możemy dziś zakończyć, ale przedtem jak zwykle polecane zasoby i linki dla dociekliwych:

– dyskusja na stackoverflow na temat działania domknięć w JavaScript:

http://stackoverflow.com/questions/111102/how-do-javascript-closures-work?rq=1

– opis domknięć w JavaScript z przykładami praktycznymi na stronach Mozilla Developer:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

– obszerny artykuł na Wikipedii na temat funkcji anonimowych z punktu widzenia wielu różnych języków programowania:

http://en.wikipedia.org/wiki/Anonymous_function

– znakomity przewodnik po języku JavaScript, opisujący szczegółowo nie tylko lambdy i domknięcia, ale i wiele innych aspektów:

http://bonsaiden.github.io/JavaScript-Garden/pl/

Zadanie szczegółowego omówienia poruszanych dziś zagadnień nie należy do prostych. Mam jednak nadzieję ze artykuł temu zadaniu sprostał najlepiej jak mógł. Oczywiście budujące dyskusje zawsze mile widziane.

Dziękuję za uwagę.

Programista WWW i aplikacji mobilnych z wieloletnim doświadczeniem, początkujący bloger. Pasjonat programowania, nowych technologii, e-commerce, a także sportu i motoryzacji.

Twitter LinkedIn Google+ Skype Xing 

Podaj dalej: Share on Facebook1Tweet about this on TwitterShare on Google+1Share on LinkedIn1Share on Tumblr0Digg thisEmail this to someonePin on Pinterest1
Możesz skomentować leave a response, lub podać trackback z własnej strony.