2014-10-27

Malo JavaScript-a

Ima jedna čudna priča o meni i znanju nekih tehnologija. Šta god uradio u istoj, ja kažem da je ne znam :). Recimo da sam shvatam da ne znam dovoljno. Jedna od tih tehnologija koju "ne znam" je JavaScript.

Tokom mog razvitka igrao sam se svačim u JavaScript-u, ali na kraju sam rešio da koristim JavaScript klase koje kontrolišu neke HTML elemente (ah, koja mudrost, sigurno postoji gomila framewokr-ova koji to isto omogućavaju, ali ja volim da se ipak sam igram). Ono što želim da uradim je sledeće: u HTML-u imam nekoliko DIV-ova, svaki od njih treba da sadrži i kontroliše nezavisni brojac. To jest treba da ispiše u DIV-u trenutnu vrednost brojača, da ponudi mogućnost reseta i mogućnost inkremenisanja tog brojača. Naravno brojači treba da budu nezavisni.

Nikakva mudrost, napravimo DIV, smestimo BUTTON koji okida neku JavaScript funkciju i eto. Mogu reći da je to dovoljno dobro, sve dok ne poželite da dodate novi brojač (mozda dinamički ...). Od jednom imamo malo kopiranja koda i tu može doći do problema. Zato ja idem drugim putem. Kada se učita HTML, ja ću da povežem DIV i neki objekat odgovarajuće klase. Ta klasa ce da ispiše sve što treba i da održava svoju instancu brojača. Problem nastaje kod samog ispisa onclick="this.increment()". To neće raditi jer se this odnosi na konkretan HTML element. Moramo nekako da prosledimo globalno ime kojom pristupamo odredjenoj instanci:

function Counter(name, draw_id)
{
  this.name = name;
  this.draw_id = draw_id;

  this.draw = function()
  {
    var html =
      'Name: '+this.name+'<br/>'+
      'Count: '+this.count+'<br/>'+
      '<button onclick="'+this.name+'.reset();">Reset</button><br/>'+
      '<button onclick="'+this.name+'.increment();">Increment</button>';
    document.getElementById(this.draw_id).innerHTML = html;
  };

  this.reset = function()
  {
    this.count = 0;
    this.draw();
  };

  this.increment = function()
  {
    this.count++;
    this.draw();
  };

  this.reset();
}

function init()
{
  c1 = new Counter('c1', 'd1');
  c1.increment();

  c2 = new Counter('c2', 'd2');
  c2.increment();
  c2.increment();

  c3 = new Counter('c3', 'd3');
  c3.increment();
  c3.increment();
  c3.increment();
}
Ovaj primer možete ovde videti: http://www.chupcko.org/blog/counter_1.html.

Šta mi se ovde ne svidja? Dva puta moramo da napišemo ime, jednom kao promenljivu, a jednom kao string. Kada bi mogli nekako ... nešto ... I tu se setim jednog malog trika, dodajem na kraju klase sledeće:

  window[this.name] = this;
I sada možemo da inicijalizujemo samo sa:
function init()
{
  new Counter('c1', 'd1');
  c1.increment();

  new Counter('c2', 'd2');
  c2.increment();
  c2.increment();

  new Counter('c3', 'd3');
  c3.increment();
  c3.increment();
  c3.increment();
}
Ovaj primer možete ovde videti: http://www.chupcko.org/blog/counter_2.html.

Kada bi mogli nekako da dobijemo za neku instancu neke klase u JavaScript-u njegovo ime ... Pa ček, JavaScript ima nešto mnogo lepo, možemo uz pomoć Object.prototype da dodajemo nove funkcije koje važe za sve objekte. Ideja je da dodamo dve funkcije, neka se jedna $id i neka vraća neki id instance objekta. A druga $name koja vraća ime (u kojem naravno dominira spomenuti id). Sve to zatvoreno u closure (zbog globalne promenljive koja se koristi za id), i uz malo keširanja:

(
  function()
  {
    var _id = 0;
    Object.prototype.$id = function()
    {
      if(typeof this.$_id === 'undefined')
      {
        this.$_id = _id;
        _id++;
      }
      return this.$_id;
    };
    Object.prototype.$name = function()
    {
      if(typeof this.$_name === 'undefined')
      {
        this.$_name = '$$_'+this.$id();
        window[this.$_name] = this;
      }
      return this.$_name;
    };
  }
)();

function Counter(draw_id)
{
  this.draw_id = draw_id;

  this.count = undefined;

  this.draw = function()
  {
    var html =
      'Name: '+this.$name()+'<br/>'+
      'Count: '+this.count+'<br/>'+
      '<button onclick="'+this.$name()+'.reset();">Reset</button><br/>'+
      '<button onclick="'+this.$name()+'.increment();">Increment</button>';
    document.getElementById(this.draw_id).innerHTML = html;
  };

  this.reset = function()
  {
    this.count = 0;
    this.draw();
  };

  this.increment = function()
  {
    this.count++;
    this.draw();
  };

  this.reset();
}

function init()
{
  var c1 = new Counter('d1');
  c1.increment();

  var c2 = new Counter('d2');
  c2.increment();
  c2.increment();

  var c3 = new Counter('d3');
  c3.increment();
  c3.increment();
  c3.increment();
}
Ovaj primer možete ovde videti: http://www.chupcko.org/blog/counter_3.html.

I na kraju malo ulepšano: http://www.chupcko.org/blog/counter_4.html.

Naravno sve ovo može i na drugi način, ali ostaviću vama da ga sami pronadjete (igranje DOM-om :) ).