czwartek, 7 lutego 2013

lamanie zasad widzialnosci zmiennych w JavaScript

W poscie zasieg zmiennych (scope) w JavaScript opisalam jak wyglada widocznosc zmiennych w JavaScript. Podstawowa regula jest dosc jasna. Konkretny zasieg jest ograniczony przez funkcje. To co jest w srodku moze korzystac z tego co jest na zewnatrz, ale to co jest na zewnatrz nie ma dostepu do tego co jest w srodku.

var abc = "abc";

function xyz() {
  var def = "def";
}

console.log(abc);
console.log(def);

Wykonanie powyzszego kodu wyswietli abc oraz blad: ReferenceError: def is not defined. Zmienna def zostala zdefiniowana ze slowkiem var, przez co nie jest zmienna globalna, tylko lokalna we wlasnym scopie oraz scopach wewnetrznych. Z zewnatrz jej nie widac. Takie podejscie nazywamy zakresem statycznym lub leksykalnym (lexical scope). Tak na prawde o tym, gdzie sa jakie zmienne i jaki maja zasieg mozemy sie dowiedziec analizujac kod programu.

To na tyle powtorzenia. Czy istnieje sposob na udostepnienie zmiennych z wnetrza funkcji do globalnej przestrzeni nazw? Sprawdzmy.

Zalozmy, ze mamy taka strukture:

var a = "a";

function foo1 () {

  var b = "b";
  console.log(b);

  function foo2 () {

    var c = "c";
    console.log(c);

    console.log(b);

  }
}

foo1();
foo2();

Zasieg globlany wie o istnieniu zmiennej a i funkcji foo1. Reszta jest przed nim ukryta o czym swiadczy blad: ReferenceError: foo2 is not defined. Naszym celem jest wyciagniecie foo2 i jej zawartosci na swiatlo dzienne czyli do globalnej przestrzeni zmiennych.

Przede wszystkim zeby to zrobic musimy zmienic troszke podejscie. Statyczna prezentacja zakresow widzialnosci zmiennych nam tu nie pomoze. Do problemu trzeba podejsc dynamicznie!! Jedno z wielu rozwiazan podalam w poscie funkcje w JavaScript, part 5 - funkcje moga zwracac funkcje:

var a = "a";

function foo1 () {

  var b = "b";
  console.log("foo1 " + b);

  return function () {

    var c = "c";
    console.log("foo2 " + c);
    console.log("foo2 " + b);
  };
}

var foo2 = foo1();
foo2();

Jak widac na konsoli mamy wypisane foo1 b, ktore pochodzi z wykonania foo1 podczas przypisania tego co ona zwraca do foo2. Nastepnie wywolanie foo2 wypisuje foo2 c oraz foo2 b. Czyli z poziomu zasiegu globalnego mamy dostep do "wnetrznosci" foo1. Takie pamietanie przez funkcje swojego kontekstu nazywamy domknieciem (closure).

Innym sposobem na wykorzystanie domkniecia jest operowanie na zmiennych globalnych wewnatrz funkcji. Czym rozni sie zmienna globalna od zmiennej lokalnej w JavaScript? Slowkiem var:

var a = "a";

function foo1 () {

  var b = "b";
  console.log("foo1 " + b);

  foo2 = function () { // nie ma var

    var c = "c";
    console.log("foo2 " + c);
    console.log("foo2 " + b);
  };
}

foo1();
foo2();

Tutaj tak samo jak w poprzednim przykladzie istnienie foo2 zalezy od wykonania foo1. Foo2 nie istnieje, dopoki nie zostanie utworzone w foo1 jako zmienna globalna. Tutaj tez mamy domnkniecie, nasza zmienna globalna foo2 pamieta kontekst w ktorym zostala utworzona i wyswietla prawidlowo zmienna b.

Brak komentarzy:

Prześlij komentarz