niedziela, 12 czerwca 2011

Domyślne parametry funkcji

Funkcja i domyślne jej parametry w JavaScript? JavaScript nigdy nie oferował mechanizmu nadawania domyślnych wartości niezdefiniowanym parametrom przy jej wywołaniu.

Można sobie z tą niedogodnością poradzić w całkiem prosty sposób i wiele osób tak to właśnie robi. Poniżej zamieszczam kawałek kodu.

var fn = function(a, b, c){
  a = a || 25;
  b = b || [2, 5];
  c = c || 'domyslny';
  alert(
    'liczba: ' + a + '\n' +
    'tablica: ' + b + '\n' +
    'string: ' + c
  );
};

fn(5, [1,2,3], 'test');
fn(5, [1,2,3]);
fn(5);
fn();

Działa całkiem nieźle. Co jednak jeśli wywołamy funkcję w poniższy sposób?

fn(0);

Funkcja wyświetla 25. Przecież wysłaliśmy 0 (zero). Dzieje się tak, ponieważ poniższa konstrukcja traktuje liczbę 0 jako wartość false, dlatego do zmiennej a przypisywana jest alternatywna wartość 25.

a = a || 25;

Skoro znamy już problemy wynikające z powyższych konstrukcji przejdźmy do rozwiązania problemu w inny, bardziej elegancki sposób.
Poniżej zamieszczam funkcję która pozwala ze zwykłej funkcji stworzyć funkcję przyjmującą domyślne parametry. Kod zawiera również funkcję testową oraz kilka jej wywołań.

Function.prototype.params = function(){
  var that = this,
      arg = [].splice.apply(arguments, [0, arguments.length]);
  return function(){
    return that.apply(this, [].splice.apply(arguments, [0]).concat(
        arg.slice(arguments.length)
      )
    );
  };
};

var test = function(a, b){
  alert(
    a + '\n' +
    b
  );
}.params(11, 22);

test(1, 2);
test(1);
test();

Stworzenie funkcji z domyślnymi parametrami jest teraz dziecinnie proste. Wystarczy zadeklarować dowolną funkcję a zaraz za nią wywołać naszą metodę z argumentami, które mają być domyślne.
Warto pamiętać o tym, że powyższe udogodnienie będzie spowalniać działanie skryptu. Każde wywołanie zwróconej funkcji powoduje wywołanie kilku dodatkowych instrukcji. Nadużywanie tego udogodnienia może okazać się nierozsądne, w szczególności w przypadku prostych, wielokrotnie powtarzanych funkcji.

wtorek, 7 czerwca 2011

JavaScript i nazwy

Po pewnym czasie programowania w tym specyficznym języku daje się zauważyć w napisanym przeze mnie kodzie ciekawą prawidłowość. Otóż nigdy (z niewielkimi wyjątkami) nie nazywam w kodzie czegoś czego używam tylko raz.
Specyfika tego języka umożliwia takie pisanie kodu i bardzo chętnie tak właśnie go piszę.
W JavaScripcie każdy obiekt, tablica, funkcja, liczba itd. może mieć przypisaną nazwę, ale może jej również nie posiadać (funkcja anonimowa, dana natychmiastowa).
Każda dodatkowa zmienna w kodzie to możliwość kolizji nazw i potencjalnych problemów, dlatego uważam, że trzymanie się powyższej zasady pozwala na całkiem przyjemne i bezproblemowe rozbudowywanie kodu.

(function(str){
  alert(str);
})([
  'JavaScript pozwala na',
  {
    x: [
      'budowanie calkiem',
      (function(x){
        return 'zlozonych ' + x[''] + ' ' + x.a;
      })({
        '': 'struktur',
        a: 'danych'
      }),
      'bez deklaracji',
      'jakiejkolwiek'
    ]
  }.x.join(' '),
  'zmiennej w kodzie.'
].join(' '));

Powyżej kawałek kodu, którym starałem się wyrazić to co mam na myśli pisząc tego posta.

poniedziałek, 6 czerwca 2011

Animacja jednostkowa

Animacja jednostkowa to funkcja którą napisałem, po to aby ułatwić sobie tworzenie animacji w swoich aplikacjach.
Nazywam ją tak, ponieważ umożliwia mi proste zbudowanie animacji dowolnej wielkości (właściwości obiektu) w prosty i elegancki sposób poprzez wywołanie mojej funkcji z parametrem - liczbą z zakresu od 0 do 1 włącznie, która określa aktualny stan animacji.
Poniżej zamieszczam kod tytułowej funkcji a zaraz za nim krótki opis sposobu używania.

var animation = function(fn, params){
  params = params || {};
  params = {
    frames: params.frames || 10,
    fn: params.fn || null,
    t: params.t || 30
  };
  var frame = 0;
  fn(0);
  var iid = setInterval(function(){
    fn( (++frame)/params.frames );
    if( frame >= (params.frames) ){
      clearInterval(iid);
      params.fn();
    }
  }, params.t);
  return iid;
};

Jak widać funkcja przyjmuje dwa parametry: wskaźnik do funkcji oraz obiekt opcjonalnych parametrów.
fn - to wskaźnik do głównej funkcji wykonawczej, która będzie decydowała o tym co będziemy animować.
Zmienna params przyjmuje 3 opcjonalne parametry:
- frames - ilość klatek animacji,
- t - interwał pomiędzy kolejnymi klatkami animacji,
- fn - funkcja, która zostanie wywołana zaraz po zakończeniu działania animacji.
Funkcja zwraca wartość Number zwracaną przez wbudowaną funkcję setInterval. Pozwala to na wcześniejsze zakończenie działania animacji.

// prosty kod pokazujacy sposob uzycia funkcji
animation(function(x){
  alert(x);
}, {
  frames: 10,
  fn: function(){
    alert('koniec');
  }
});

Jak widać funkcja jest niczym więcej jak wzbogaceniem funkcji setInterval o dodatkowe funkcjonalności.
Mimo wszystko potrafi bardzo uprościć pisany kod.
Do jej głównych zalet można zaliczyć:
- zwracany bieżący stan animacji mieszczący się zawsze w przedziale domkniętym <0; 1>,
- pierwsze wywołanie funkcji fn(0) jest wykonywane synchronicznie, w przeciwieństwie do zastosowania samej funkcji setInterval, gdzie na pierwsze wywołanie zadanej funkcji skrypt musi odczekać zadany czas. Dzięki temu unikniemy krótkotrwałych stanów nieustalonych przebiegu animacji,
- i wreszcie możliwość dodania wskaźnika do funkcji wywoływanej na zakończenie animacji - prosty sposób na wykrycie końca animacji bez żadnych dodatkowych instrukcji warunkowych.