ugrás a tartalomhoz

Javascript Objektumok származtatása

Soolt · 2009. Dec. 5. (Szo), 18.15
Sziasztok!

Az objektumok öröklésének utolsó lépésénél vagyok elakadva. Igazából nem értem annak szükségességét, mivel nélküle is tökéletesen működik!

Tehát ahogy én szoktam:

//létrehozom az ősosztályt
function ClassA(a){
    this.a = a;
  }
//létrehzozok mégegyet az érdekesség kedvéért  
  function ClassB(a){
    this.b = a;
  }
//Aztán létrehzom a gyerek osztályt kiterjesztve a fent látrehozott 2 szülővel  
  function ClassC(a, b){
  	ClassA.call(this, a);
  	ClassB.call(this, b);
  	
  	this.getA = function(){
      return this.a;
    };
//csinálok valami saját metódusokat, hogy válztozzon    
    this.getB = function(){
      return this.b;
    };
    
    this.setA = function(a){
      this.a = a;
    };
    
    this.setB = function(b){
      this.b = b;
    };
  }
//aztán hogy a teszt kiterjettebb legyen a gyereknek a prototypeön keresztűl is
//definiálok mégy egy metódust

  ClassC.prototype.bakker = function(){
    return this.a+"; "+this.b
  }
//aztán létrehozok valami ojjektumokat
  var ca = new ClassA(555);

  var bakker = new ClassC(1, 2);
  var bakker1 = new ClassC(3, 4);
//aztán csinálok valami beállításokat csak, hogy tesztelhessem, hogy az egyik 
//ojjektumban való módosítgatások változással lessznek-e egy másik gyerekre
//mert ugye a setA az egyik szülő mezőjét változtatja
  bakker.setA(8);
//aztán szépen kiíratom, hogy lám mi történik és minden tökéletesen megy
  alert(bakker.bakker()+"; "+bakker1.bakker()+"; "+ca.bakker);
Mások (a dokumentációk ezt megtoldják egy sorral)

  function ClassA(a){
    this.a = a;
  }
  
  function ClassB(a){
    ClassA.call(this, a);
    //....
  }
// s akkor itt jön, amit én nem értek
 ClassB.prototype = new ClassA();

A kérdésem a következő! Minek az utolsó sor? ClassB.prototype = new ClassA(); Az utolsó sor nélkül is tökéletes az öröklés. A gyerekben való módosítgatások nem lessznek hatással a szülőre és egy mésik gyerekben való módosítgatás sem befoljásolja az egyik gyereket!

A magyarázatot előre is köszönöm!
 
1

prototype chain, OOP inheritance

toxin · 2009. Dec. 6. (V), 11.35
ha az örökítés prototype láncoláson keresztül valósítod meg pl. alapestben

		function ClassA(_a){
			this.a = _a;
		}		
		ClassA.prototype.getA = function(){
			return this.a;
		}			
		
		function ClassB(_a,_b){
			ClassA.call(this,_a);
			this.b = _b;
		}		
		ClassB.prototype = new ClassA();
		ClassB.prototype.getB = function(){
			return this.b;
		}
		
		function ClassC(_a,_b,_c){
			ClassB.call(this,_a,_b);
			this.c = _c;
		}		
		ClassC.prototype = new ClassB();
		ClassC.prototype.getC = function(){
			return this.c;
		}
		
		var myC = new ClassC("1","2","3");
		(console && console.log || alert)(myC.getA() + ":" + myC.getB() + ":" + myC.getC()); 
ilyenkor azaz, prototype láncoláskor a proto objektumban lévő constructor tagváltozó rossz konstruktor függvényre mutat, pl. a myC.constructor-a ClassA(_a)-ra , ennek utánaállítunk:

		function ClassA(_a){
			this.a = _a;
		}		
		ClassA.prototype.getA = function(){
			return this.a;
		}			
		
		function ClassB(_a,_b){
			ClassA.call(this,_a);
			this.b = _b;
		}		
		ClassB.prototype = new ClassA();
		ClassB.prototype.constructor = ClassB;
		ClassB.prototype.getB = function(){
			return this.b;
		}
		
		function ClassC(_a,_b,_c){
			ClassB.call(this,_a,_b);
			this.c = _c;
		}		
		ClassC.prototype = new ClassB();
		ClassC.prototype.constructor = ClassC;
		ClassC.prototype.getC = function(){
			return this.c;
		}
		
		var myC = new ClassC("1","2","3");
		(console && console.log || alert)(myC.getA() + ":" + myC.getB() + ":" + myC.getC()); // 1:2:3		
		(console && console.log || alert)(myC.constructor == ClassC); //true
továbbá a ClassB.call, ClassC.call hívásokat is lehet egységesíteni, ekkor jön a konstruktor láncolás (constructor chaining) , illetve nem árt ha az esetlegesen a gyermek osztályban felülírt tagmetódust is eltudjuk érni

		function ClassA(_a){
			this.a = _a;
		}		
		ClassA.prototype.getA = function(){
			return this.a;
		}			
		
		function ClassB(_a,_b){
			ClassB.superclass.constructor.call(this,_a);
			this.b = _b;
		}
		ClassB.superclass = ClassA.prototype;		
		ClassB.prototype = new ClassA();
		ClassB.prototype.constructor = ClassB;		
		ClassB.prototype.getB = function(){
			return this.b;
		}
		
		function ClassC(_a,_b,_c){
			ClassC.superclass.constructor.call(this,_a,_b);
			this.c = _c;
		}
		ClassC.superclass = ClassB.prototype; 
		ClassC.prototype = new ClassB();
		ClassC.prototype.constructor = ClassC;
		ClassC.prototype.getB = function(){
			(console && console.log || alert)("getB has been overwritten");
			return ClassC.superclass.getB.call(this);
		}		 
		ClassC.prototype.getC = function(){
			return this.c;
		}
		
		var myC = new ClassC("1","2","3");
		(console && console.log || alert)(myC.getA() + ":" + myC.getB() + ":" + myC.getC()); // 1:2:3		
		(console && console.log || alert)(myC.constructor == ClassC); //true
lehet egyszerűsíteni, ha az öröklődési folyamatot függvénybe zárjuk, ehhez előbb vezesük be Douglas Crockford method method-ját, azaz
Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};
ami valóban csak szintaktikus cukor, azaz kevesebbet kell gépelni, ekkor a kódunk emigyen fog kinézni :
    Function.prototype.method = function(name, func){
        this.prototype[name] = func;
        return this;
    };
    
    function ClassA(_a){
        this.a = _a;
    }			
    ClassA.method("getA",function(){
        return this.a;
    })
                
    function ClassB(_a, _b){
        ClassB.superclass.constructor.call(this, _a);
        this.b = _b;
    }
    
    ClassB.superclass = ClassA.prototype;
    ClassB.prototype = new ClassA();
    ClassB.prototype.constructor = ClassB;
    ClassB.method("getB",function(){
        return this.b;
    })
                
    function ClassC(_a, _b, _c){
        ClassC.superclass.constructor.call(this, _a, _b);
        this.c = _c;
    }
    
    ClassC.superclass = ClassB.prototype;
    ClassC.prototype = new ClassB();
    ClassC.prototype.constructor = ClassC;
    ClassC.method("getB",function(){
        (console && console.log || alert)("getB has been overwritten");
        return ClassC.superclass.getB.call(this);
    }).method("getC",function(){
        return this.c;           		
    });
                
    var myC = new ClassC("1", "2", "3");
    (console && console.log || alert)(myC.getA() + ":" + myC.getB() + ":" + myC.getC()); // 1:2:3		
    (console && console.log || alert)(myC.constructor == ClassC); //true
és akkor az öröklődés függvényesítése:
function extend(subClass, superClass){
    var F = function(){};
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.prototype.constructor = subClass;
    subClass.superclass = superClass.prototype;
    if (superClass.prototype.constructor == Object.prototype.constructor) {
        superClass.prototype.constructor = superClass;
    }
}
ahol az első két sorban :

 var F = function(){};
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.prototype.constructor = subClass;
megszabadulunk az ősosztály erőforrás igényes, és fölöslegesen lefutó konstruktorától, egy üressel helyettesítve azt, maradék megegyezik az eddigiekben is alkalmazott lépésekkel
Function.prototype.method = function(name, func){
    this.prototype[name] = func;
    return this;
};

function extend(subClass, superClass){
    var F = function(){};
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.prototype.constructor = subClass;
    subClass.superclass = superClass.prototype;
    if (superClass.prototype.constructor == Object.prototype.constructor) {
        superClass.prototype.constructor = superClass;
    }
}

function ClassA(_a){
    this.a = _a;
}

ClassA.method("getA", function(){
    return this.a;
})

function ClassB(_a, _b){
    ClassB.superclass.constructor.call(this, _a);
    this.b = _b;
}

extend(ClassB,ClassA);
ClassB.method("getB", function(){
    return this.b;
})

function ClassC(_a, _b, _c){
    ClassC.superclass.constructor.call(this, _a, _b);
    this.c = _c;
}

extend(ClassC,ClassB);
ClassC.method("getB", function(){
    (console && console.log || alert)("getB has been overwritten");
    return ClassC.superclass.getB.call(this);
}).method("getC", function(){
    return this.c;
});

var myC = new ClassC("1", "2", "3");
(console && console.log || alert)(myC.getA() + ":" + myC.getB() + ":" + myC.getC()); // 1:2:3		
(console && console.log || alert)(myC.constructor == ClassC); //true

, de lehet a klasszikus OOP öröklődést is szimulálni js alatt

üdv Csaba

ps: amire még érdemes figyelni az a proto-ból örökölt tagok aszimmetrikus írás/olvasása lap aljától
2

lehet, hogy csak én vagyok túl fáradt hozzá :)

Soolt · 2009. Dec. 9. (Sze), 22.10
Nagyon szépen köszönöm a gyors választ és a magyarázzatokat. Így már sokkal érthetőbb a dolog és fene tudja, de mintha érteném is hogy miről van szó (vagy mégsem?!) (Sajnos a reggeltől szinte estig tartó koncentráció képes annyi energiát elvenni tőlem, hogy ne tudjak értékesen oda figyelni és teljesen átlátni a dolgot, ezért elnézést kérek az értetlenkedéseimért)

sóval:

Felfogtam, hogy prototype láncolaton keresztűl a konstruktor nem oda fog mutatni, ahova szeretnénk ézért szintén a prototypeon keresztül meg kell mutassuk a konstruktornak hogy hé te figyu...?! Ide mutass ne oda, mert az oda az nem te vagy.

Viszont a kérdésem arra szóllt, hogy a construktor ide oda mutogatásának a beállításán túl a:

function ClassA(a){  
    this.a = a;  
  }  
    
  function ClassB(a){  
    ClassA.call(this, a);  
    //....  
  }
Miért nem elég?! miért szokták utánna bigyeszteni a:
 ClassB.prototype = new ClassA();
sort! Mi lessz ettől másképp?!
Kihangsulyozom: ha nem akarok a konstruktor után állítani.

Tehát az alábbi 2 kód miben külömbözik?!

Ez:

function ClassA(a){  
    this.a = a;  
  }  
    
  function ClassB(a){  
    ClassA.call(this, a);  
    //....  
  }
  
  ClassB.prototype = new ClassA(); 
Mitől lessz több ennél a kódnál?!

function ClassA(a){  
    this.a = a;  
  }  
    
  function ClassB(a){  
    ClassA.call(this, a);  
    //....  
  }
Merthogy a témaindító kicsinyke példával (persze valamivel többminet írtam bele itthon a tszt kedvéért csak az egyszerűség kedvéért a minimumot másoltam ide) Kipróbáltam mindkét verziót és semmi külömbséget nem tapasztaltam!
3

Prototípus alapú öröklődés

vbence · 2009. Dec. 9. (Sze), 23.30
Íme az öröklődés a JS-ben...
Szulo = function (a, b) {  
    this.a = a;
    this.b = b;  
}  
  
Szulo.prototype.osszead = function () {
    return this.a + this.b;
}  
  
Szulo.prototype.kivon = function () {
    return this.a - this.b;
}  
  
Kiterjesztett = function (a, b) {
    Szulo.apply(this, arguments);
}  
Kiterjesztett.prototype = new Szulo();

Kiterjesztett.prototype.szoroz = function () {
    return this.a * this.b;
}  
  
Kiterjesztett.prototype.oszt = function () {
    return this.a / this.b;
}  
A Kiterjesztett.prototype = new Szulo(); valósítja meg magát a származtatást, enélkül a Szulo metódusai maradnának ahol vannak. Így kásőbb is ráaggathatunk metódusokat a szülőre, az látszani fog minden leszármazottban.

Az, hogy a konstruktorban "gyúrjuk össze" az objektumot, annak ügyes kihasználása, hogy szkriptnyelvről van szó, ha jól valósítod meg, kiköszöblheted hogy valaha is hallj a "prototype"-ról, de ha szeretnéd kihasználni a prototípus alapú öröklődés előnyeit... megteheted.
4

Javascript prototype versus closure execution speed

toxin · 2009. Dec. 10. (Cs), 07.57
kíváncsiságból azért rákerestem mit mond rá a nagy testvér, és ahogy várható is volt, a teszt szerint legalábbis, igen nagy számú hasonló objektumnál már kijön a különbség, - mondjuk a bejegyzés címével ellentétben ez nem a klauzúra, hanem a dugjunk mindent a konstruktorba, és készítsünk rengeteg másolatot hibája szvsz -

When this is run in IE7 on my machine, the closure implementation takes around 450ms and prototype takes around 140ms. Add two more empty functions and the closure time increases to 560ms and prototype to 155ms.

On Firefox 1.5.0.9 the difference is in the listed example is even larger with the closure taking 515ms and prototype still around 140ms.

For small applications that don't construct hundreds or thousands of the same type of object the execution speed difference between closure and prototype probably doesn't matter so as with all performance advice measure, measure, measure.

http://blogs.msdn.com/kristoffer/archive/2007/02/13/javascript-prototype-versus-closure-execution-speed.aspx

szóval érdemes méricskélni, és nem száműzni agyunk poros padlására a prototype -ot :), üdv Csaba
5

re

toxin · 2009. Dec. 10. (Cs), 10.11
Mitől lessz több ennél a kódnál?!


attól, hogy

 function ClassA(a){    
     this.a = a;    
   }    
       
   function ClassB(a){    
    ClassA.call(this, a);    
     //....    
   }  
     
   ClassB.prototype = new ClassA(); 
esetében, ClassA konstruktor függvény prototype objektumának kibővített elemei, láthatóak lesznek ClassB (és példányai) alatt is, ergo minden közös elemet érdemet bepakolni proto-ba, és csak a különbséget beledugdosni a konstruktor függvényekbe (meg a tömböket és objektumokat, de ez js bug, és a tulajdonságok aszinkron írása-olvasása - legalábbis ennek 'érdekes' viselkedése - tehet róla)

üdv Csaba

ui.: további érdekességek a js világából, a YUI moziban Douglas Crockford — Advanced JavaScript , nem új de aktuálist, thx ie6 ...
6

Hatalama téháx! De inkább köszönöm!!!

Soolt · 2009. Dec. 10. (Cs), 22.27
Köszönöm a frappáns válaszokat!!! Mindekinek nagyon köszönöm és most már azt hiszem neki rugaszkodhatok ennek a cikknek. Nektek köszönhetően sikerült pótolni a hiányosságaimat a cikk megértésséhez :)