Kurs obiektowego JavaScript (14)

Autor: Damian Chodorek • Opublikowany: 1 października 2014 • Ostatnia aktualizacja: 8 lutego 2015 • Kategoria: javascript, kursy

Łańcuch zasięgu i jego przerywanie. Gettery, settery.

Jak pamiętasz z poprzednich lekcji, w JS nie ma zasięgu blokowego. Zmienna zdefiniowana wewnątrz funkcji jest niewidoczna poza nią, ale jeśli jest zdefiniowana w bloku (np. if lub for) jest widoczna poza nim.

Jeżeli zdefiniujemy funkcję b() wewnątrz funkcji a(), to będzie ona mieć dostęp do do wszystkich zmiennych w a(), za to a() nie będzie mieć dostępu do zmiennych b().

var x1=1;
function a(){
  var x2=2;
  function b(){
    var x3=3;
    x2; //dostęp poprawny
    x1; //dostęp poprawny
  }

  //tutaj a() nie ma dostępu do x3
}

//tutaj x1 nie widzi ani x2, ani x1

Zrozumienie powyższego przykładu jest bardzo ważne jeśli chcesz zostać dobrym programistą JS (a chcesz, skoro to czytasz). Jeśli masz z tym problemy, to zachęcam to przetestowania kodu, zagłębienia się w to co napisałem i zapamiętania przykładu. Będzie Ci to za chwilę potrzebne.

Zerwanie łańcucha zasięgu

Wyobraź sobie sytuację, w której w zasięgu globalnym zdefiniowano zmienną a oraz funkcję F(). W niej z kolei zdefiniowano zmienną b oraz funkcję N(), w której utworzono zmienną c. Tak jak poniżej.

var a=1;

function F(){
  var b=2;
  function N(){
    var c=3;
  }
}

Dla zmiennej a widoczna jest wyłącznie funkcja F(). Dla b widoczne jest a oraz funkcja N(). Dla c widoczne jest wszystko. Teraz wyobraź sobie sytuację, w której jakimś cudem N() znalazło się w zasięgu globalnym. Wówczas będzie na tym samym poziomie co a. Funkcje pamiętają swój pierwotny zakres, więc N() będzie miało dostęp do F() i b. Pomimo, że a jest na tym samym poziomie co N(), to nie będzie widzieć zmiennej b.

Zapewne nasuwa Ci się pytanie – w jaki sposób N() miałoby się znaleźć w zasięgu globalnym? Zaraz się tego dowiesz.

Jak przerwać łańcuch zasięgu

Przyjrzyj się poniższej funkcji.

function f(){
  var b="b";
  return function(){
    return b;
  }
}

Znajduje się w niej lokalna zmienna b. Jest więc niewidoczna z punktu widzenia przestrzeni globalnej. Funkcja f() zwraca inną funkcję, która ma dostęp do zmiennej b. Tak naprawdę zwracana funkcja jest przypadkiem omówionym kilka akapitów wcześniej (jest funkcją N()). Pomimo, że dostanie się do przestrzeni globalnej (bo zwróci ją f()), to nadal będzie miała dostęp do b. Spójrz na przykład poniżej.

var n=f();
//n ma dostęp do b:
n(); //"b"

Podkreślam raz jeszcze, że zdefiniowana w danym miejscu funkcja zapamiętuje swój zakres, niezależnie od tego, gdzie się później znajdzie.

Teraz osiągniemy ten sam efekt w inny sposób. f() nie będzie zwracać funkcji, ale utworzy zmienną globalną n() w swoim ciele.

var n;
function f(){
  var b="b";
  n=function(){ //nie ma var więc n będzie globalne
    return b;
  }
}

f(); //wywołanie f(), powoduje przypisanie funkcji do n
n(); //"b", wynik jak poprzednio

Do tej pory poznałeś dwa sposoby na przerwanie łańcucha zasięgu. Przed Tobą jeszcze jeden przykład, który zilustruje sposób w jaki działa zasięg funkcji.

function f(argument){
  var n=function(){
    return argument;
  }
  ++argument;
  return n;
}

var m=f(123);
m(); //124

Zwróć uwagę na to, że instrukcja ++argument została wykonana gdy wywołano f(), ale m() ciągle ma dostęp do aktualnej wartości zmiennej argument. To dlatego, że argumenty funkcji również są traktowane jak zmienne lokalne.

Przerwania w pętli

Poniżej funkcja, która w pętli tworzy nowe funkcje zwracające wartość licznika. Nowe funkcje będą dodawane do tablicy, która zostanie zwrócona na końcu.

function f(){
  var arr=[];
  var i;
  for(i=0; i<3; ++i){
    arr[i]=function(){
      return i;
    }
  };
  return arr;
}

var a=f();

a[0](); //3
a[1](); //3
a[2](); //3

Jeżeli zrozumiałeś wcześniejszy materiał, to wynik powyższego kodu jest dla Ciebie oczywisty. Funkcje z tablicy zwracają referencję do licznika i, ten z kolei po wykonaniu f() posiada wartość 3.

Jeśli chcielibyśmy, aby funkcje zwracały kolejne liczby: 0, 1, 2, to poniższy kod jest dobrym przykładem jak to zrobić.

function f(){
  var arr=[];
  var i;
  for(i=0; i<3; ++i){
    arr[i]=(function(x){
      return function(){
        return x;
      }
    })(i);
  }
  return arr;
}

var a=f();

a[0](); //0
a[1](); //1
a[2](); //2

Teraz do tablicy przypisujemy funkcję, którą zwraca inna funkcja z argumentem. Jak pamiętasz, argumenty są traktowane jak lokalne zmienne. Są tworzone na bieżąco i dlatego trzymają wartość przekazaną w trakcie wywołania.

Gettery i settery

Naszym zadaniem jest ukrycie zmiennej i utworzenie dwóch funkcji, których zadaniem będzie nadawanie i zwracanie wartości tej zmiennej. Kod poniżej.

var getX, setX;
(function(){
  var x=0;
  getX=function(){ return x; };
  setX=function(nx){ x=nx; };
})()

Teraz, z zakresu setX() oraz getX() nie ma bezpośredniego dostępu do zmiennej x. Jej wartość można zmienić lub dostać wyłącznie przy użyciu zdefiniowanych funkcji.

Iteratory

Iteratorem na potrzeby tej lekcji nazwiemy funkcję, która zwróci kolejny element podanej tablicy. Nasz przykład będzie bardzo prosty, ale w praktyce iterator może poza zwróceniem wartości, wykonywać jakieś dodatkowe działania. W tym celu także skorzystamy z poznanej reguły: funkcje pamiętają swój zakres.

function getIterator(arr){
  var i=0;
  return function(){
    return arr[i++];
  };
}

//teraz należy stworzyć iterator
var next=getIterator(['a', 'b', 'c', 'd']);
next(); //'a'
next(); //'b'
next(); //'c'
next(); //'d'

Materiał, który poznałeś do tego momentu, był przygotowaniem do obiektowości w JavaScript. Temat ten rozpocznę od kolejnej lekcji.

W tym artykule to wszystko. Przejdź do następnej części:

Kurs obiektowego JavaScript (15)

Zobacz również
Kurs obiektowego JavaScript (12)Przedstawienie funkcji anonimowych i zwrotnych (callback).
Kurs obiektowego JavaScript (13)Wywołanie funkcji przy definicji, zagnieżdżanie, zwracanie i nadpisywanie funkcji.
Kurs obiektowego JavaScript (15)Wstęp do obiektów. Omówienie pól i metod. Konstruktory i słowo kluczowe new.
Kurs obiektowego JavaScript (16)Obiekt globalny. Pole constructor. Operator instanceof. Przekazywanie i porównywanie obiektów.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.