spájame
slovenskú
IT komunitu
pridaj sa
Registrácia · Login

Jan Ranostaj 3.2.2012, článok je súčasťou seriálu OOP v Javascripte
Hodnoť článok:
0 0

OOP v Javascripte [2] - Objekt Prototype

Ďalší z prístupov OOP v javascripte, tentokrát budeme rozširovať základný objekt pomocou objektu prototype.

V predchádzajúcej časti sme si popísali základné typy objektov a ich rozdiely.

Poďme teraz ďalej a ukážme si ako rozšíriť náš objekt Person rýchlo a efektívne stým, že zachováme základnú funkcionalitu a zároveň udržíme prehladnosť a modularitu objektu.


Čo vlastne je ten Prototype?

Úplne po lopate povedané, prototype je objekt, ktorý nám pohodlne umožňuje pridať akúkoľvek property (vlastnosť) objektu ktorá je ihneď dostupná pre všetky jeho inštancie.

Zoberme si napr. náš konštruktor objekt Person:

var Person = function(name,age) {

   this.name = name;
   this.age = age;
   this.view = function() {
        console.log(this.name + ',' + this.age);
   }

};

Chceme tento objekt rozšíriť o getName() metódu, ako budeme teda postupovať?

1. Bez objektu prototype....

var Jano = new Person('Jano', 31)
Jano.getName = function() { return this.name };
Jano.getName() // return 'Jano'

Super! a pridali sme metódu getName() k inštancii Jano. Problém však nastane ak máme viac inštancií objektu Person:

var Jano = new Person('Jano', 31)

Jano.getName = function() { return this.name };
Jano.getName() // return 'Jano'

var Peto = new Person('Peto', 31)
Peto.getName() //  Error!

Peto.getName() nám samozrejme vráti error pretože metódu getName() sme priradili inštancii Jano a nie objektu Person.

2. Riešenie s protoype...

Person.protoype.getName = function()
{
   return this.name;
}

var Jano = new Person('Jano', 31)
Jano.getName() // return 'Jano'

var Peto = new Person('Peto', 31)
Peto.getName() //  return 'Peto'

Tak a teraz všetky inštancie majú dostupnú metódu getName()

Problém dedičnosti

Obvykle objekty majú komplexnejšie štruktúry ako napr. objekt s nejakou property (vlastnosťou), ktorá je ďalším objektom. Problém nastane keď definujeme property (vlastnosť) pomocou prototype, ktorý sa odkazuje na nejaký komplexnejší objekt, pretože táto vlastnosť bude dedená všetkými jeho inštanciami.
Tak napr.:


var Person = function(){};

Person.prototype.data  = {name: 'Jano', sex: 'Male' };

Person.prototype.setData = function(name,sex) {
  this.data.name = name;
  this.data.sex = sex;
}

Person.prototype.getData = function(){
  console.log(this.data.name+': '+this.data.sex)
}

var muz  = new Person();
muz.setData("Jozo", "Male");
muz.getData() // Jozo: Male

var zena  = new Person();
zena.setData("Katka", "Female");
zena.getData() // Katka: Female

// znova zavolame objekt muz
muz.getData() // Katka: Female  ?? nemalo by to byt  Jozo: Male ??

Čo sa stalo?

Obidva objekty muz a zena nemaju svoju vlastnú property (vlastnosť) data, takže ju dedia z objektu Person.prototype. Pretože muz.data a zena.data sa odkazujú (sú pointers) na ten istý objekt Person.prototype, akákoľvek zmena v jednej inštancii sa preto prenáša do ďalších inštancií.

Tento problém však môžeme veľmi jednoducho ošetriť tým, že inštanciám vytvoríme svoju vlastnú property (vlastnosť) data na úrovni konštruktora:


var Person = function(){

  // vytvorime vlastnu property objektu
  this.data = {name: 'Jano', sex: 'Male' }

};

// toto odstranime -> Person.prototype.data  = {name: 'Jano', sex: 'Male' };

Person.prototype.setData = function(name,sex) {
  this.data.name = name;
  this.data.sex = sex;
}

Person.prototype.getData = function(){
  console.log(this.data.name+': '+this.data.sex)
}

var muz  = new Person();
muz.setData("Jozo", "Male");
muz.getData() // Jozo: Male

var zena  = new Person();
zena.setData("Katka", "Female");
zena.getData() // Katka: Female

// znova zavolame objekt muz
muz.getData() // Jozo: Male  This works!

Vysvetlenie

Pretože this vo vnútri konštruktora odkazuje na seba samého, (teda ak máme inštanciu tak odkazuje na seba samu) nastavením this.data sa priradí inštanciám vlastná data property (vlastnosť). Takže zmena vastnosti v nejakej inštancii nám už nebude robiť problémy v ostatných inštanciách.

Zjednodušený prototype model

Tento model spočíva vtom že umožnuje vytvorť objekt použitím akéhokoľvek iného objektu (aj z objektu literal) ako objekt prototype.

Objekt literal


var Person = {

    age: "30",
    first_name: "",
    last_name:  "",
    interests: [],

    add_interests: function() {

         for(var i=0;i<=arguments.length;i++) {

             this.interests.push(arguments[i]);
         }
    }

}

Vyvorili sme si objekt literal, a teraz ho chceme nejak rozumne rozšíriť o ďalšie vlastnosti, stým že chceme aby aj objekty ktoré ho dedia, prebrali všetky jeho vlastnosti.

Vytvorime nový objekt stým že preberieme všetky vlastnosti z objektu Person

var Father = Object.create(Person)

Father.first_name = "Jano";
Father.last_name = "Ranostaj";
Father.add_interests("Superman");

// pridame novu vlastnost
Father.whole_name = function() {
     return this.first_name+' '+this.last_name;
}

console.log(Father);
//  Object {first_name="Jano", age="30", last_name="Ranostaj", interests=["Superman"], add_interests=function(),  whole_name=function() }

Objekt Son prebrá vlastnosti z objektu Father

var Son = Object.create(Father)

Son.first_name = "Marek";

console.log(Son.whole_name()); // Marek Ranostaj

Kompatibilita

Keďže prístup pomocou Object.create je nová špecifikacia v ECMAScript 5 nie každý prehliadač ju musí podporovať.
Aby sme sa vyhli problémom musíme zadefinovať nasledovnú podmienku:

if(typeof Object.create !=='function') {
    Object.create = function(o) {
      function F(){}
      F.prototype = o;
      return new F();
    }

}

Príklad na záver

Na príklade si ukážeme ako by zjednodušený prototype model asi mohol fungovat v praxi. Pre tento príklad použijem knižnicu MooTools, to isté sa dá v samozrejme spraviť aj s jQuery.
Základný objekt BannerBase (objekt literal), ktorého základná funkcia je rotovanie nejakých bannerov s FadeIN a FadeOUT efektom:


    var BannerBase = {

        banners: 'banners',
        count: 0,
        duration: 1500,
        durationEfect: 1000,

        // spúšťač
        init: function()
        {
             this.items= this.banners.length;

             this.fadeIn(0);
             setInterval(this.run.bind(this),this.duration)

        },

        run:  function()
        {
            this.fadeOut(this.count)

            this.count ++;
            if( this.items==this.count)
            {
               this.fadeIn(0)
               this.count = 0;
            } else {
              this.fadeIn(this.count)
            }

            return this.count;
        },

        fadeIn: function(index)
        {
             this.banners.setStyle('opacity',0);

             this.effect(index,0,1)
             // custom event
             this.banners[index].fireEvent('fadeIn', index);

        },

        fadeOut: function(index)
        {
             this.effect(index,1,0)
             // custom event
             this.banners[index].fireEvent('fadeOut', index);

        },

       effect: function(index, from,to)
       {
           var Effect = new Fx.Tween(this.banners[index], {duration:this.durationEfect})
           Effect.start('opacity',[from,to]);

       },

    }

Rozšírime objekt: BannerBase

Týmto zápisom vlastne vytvoríme nový objekt BannerAdvanced, stým že preberieme všetky vlastnosti z objektu BannerBase plus pridáme ďalšie nové vlastnosti, ktoré "odpálime" pomocou Custom Events, tak že pri udalostiach fadeIn a fadeOut sa vypíše text do banneru.

 // preberieme vlastnosti objektu BannerBase
var BannerAdvanced = Object.create(BannerBase);

// vytvorime novu vlastnost onFadeIn
BannerAdvanced.onFadeIn = function() {
      $$(this.banners).addEvent('fadeIn', function(e) {
          $(this).set('text', $(this).get('class')+' box is fading IN...')
    })
}

// vytvorime novu vlastnost onFadeOut
BannerAdvanced.onFadeOut = function() {
      $$(this.banners).addEvent('fadeOut', function(e) {
          $(this).set('text', $(this).get('class')+' box is fading OUT...')
    })
}

A na koniec finálny objekt: BannerFinal

BannerFinal dedí všetky vlastnosti z BannerBase a BannerAdvanced

// preberieme vlastnosti objektu BannerAdvanced
var BannerFinal = Object.create(BannerAdvanced);

// nastavime dobu Intervalu
BannerFinal.duration = 3000;

// nastavime na ktore elementy budeme rotovanie aplikovat
BannerFinal.banners = $$('#banners div');

// spustime rotovanie
BannerFinal.init();

// spustime custom Events z objektu BannerAdvanced
BannerFinal.onFadeIn();
BannerFinal.onFadeOut();

Demo

Pokračovanie...

Pozrieme sa trošku na vytváranie Class s MooTools, prístup pomocou Modulov a ešte uvidím čo ma napadne, samozrejme aj nejaké examples...

Jan Ranostaj Jan Ranostaj

Programátor, vývojár aplikácii.


Hodnoť článok:
0 0

6 komentárov k článku:

Komentovať môžu iba prihlásení

Zaregistruj sa cez bezplatnú registráciu alebo použi login cez Facebook (FB Connect)

Prihlás sa tu, ak už máš profil na Zajtra.sk:


Zabudol som heslo

0 0 Matej Víťaz 31.1.2014 02:48:29
Pár krát tam máte namiesto "prototype" napísané "protoype". Inak super článok.
0 0 iwtu 4.8.2012 10:00:08
Zjednodušený prototype model. Tento model sa lisi v tom, ze kym to cez prototype sa kod funkcie nacita iba raz, vo "Vasom zjednosenom prototype" sa bude kazda funkcia nacitavat pri kazdom vytvoreni noveho objektu.

mimochodom, ohladom Javascriptu odporucam pozriet nasledujuce videa:
http://www.youtube.com/watch?v=seX7jYI96GE
http://www.youtube.com/watch?v=hQVTIJBZook&feature=related
0 0 Neo 6.2.2012 16:43:41
Nazdar, zaujimava tema.

Person.prototype.data toto je v podstate static property nie?
0 0 Jan Ranostaj 4.2.2012 14:07:58
@Michal, prepac ale celkom nerozumiem tvojej otazke.
@mhp - zo zaciatku som otom nevedel ani ja, JS som bral ako nutne zlo moc som to neobluboval, no s OOP som vtom nasiel urcitu logiku a krasu :)
0 0 Michal Hadár 3.2.2012 23:35:06
nieje tak nahodou Prototype na komercne ucely trosku pridrahe?
0 0 - 3.2.2012 10:22:38
sice som nestihol precitat este do konca clanku, ale fajnova tema :). ked som zacinal s web dev, ani som netusil, ze s JS sa da OOP-vat. a zo skusenosti viem, ze som nebol jediny, kto o tom nemal tusenia, ked zacinal. takze dakujem aj za zacinajucich ;).
Zajtra.sk > Programovanie > Seriály > OOP v Javascripte [2] - Objekt Prototype


Kritika

Vieš ako robiť veci lepšie? Pomôž našim odvážnejším členom a skritizuj im projekty!

Reklama

Seriály zo Zajtra.sk

Reklama