Programování v jazyce JavaScript Katedra softwarového inženýrství Fakulta informačních technologií České vysoké učení technické v Praze Pavel Štěpán, 2011 Dědičnost objektů BI-JSC Evropský sociální fond Praha & EU: Investujeme do vaší budoucnosti P. Štěpán PHP BI-PHP, výpis 8 1/8
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title></title> <meta http-equiv="content-type" content="text/html; charset=utf-8"> </head> <body> <h1>objekty: prototypy, instance, dedicnost</h1> <script type="text/javascript"> Prototyp: kazda funkce ma vlastnost protype. To je pointer na objekt, ktery by mel obsahovat vlastnosti a metody, pristupne vsem instancim pro dany konstruktor. Vlastnosti a metody se tedy prirazuji prototypu, nikoli v konstruktoru. function Clovek(){ // prazdny konstruktor // vlastnosti a metody sdilene vsemi instancemi Clovek.prototype.id = "5"; Clovek.prototype.jmeno = "Karel"; // vlastnost referencniho typu (pole)!! Clovek.prototype.jazyky = ["Java","PHP"]; Clovek.prototype.vypisJmeno = function(){ var clovek5 = new Clovek(); // clovek5.vypisjmeno(); Funguje takto: pri vytvoreni instance (new) se do instanci vlozi odkaz na objekt prototyp (v nekterych prohlizecich pristupna programatorovi pod jmenem proto ). Pri hledani vlastnosti (metody) pro cteni hleda dany nazev primo v instanci. Je-li nalezena, pouzije se. Pokud ne, zacne hledat (pomoci pointeru na prototyp) v prototypu. Neni-li vlastnost proto pristupna programatorovi, lze pouzit: alert(clovek.prototype.isprototypeof(clovek5)); // true V prototypu je i odkaz na construktor, takze i zde lze zjistovat, o jakou "instanci" jde. // Pozor: pridam-li vlastnost do instance (primo), // prekryji dosavadni vlastnost v prototypu! clovek5.jmeno = "Venca"; // clovek5.vypisjmeno(); // Venca // prekryvajici vlastnost lze odstrani operatorem delete // delete clovek5.jmeno; // clovek5.vypisjmeno(); // Karel P. Štěpán PHP BI-PHP, výpis 8 2/8
// zjisteni, zda jde o vlastnost instance nebo prototypu // alert(clovek5.hasownproperty("jmeno")); // pro instancni true // problem prototypu u vlastnosti referencniho typu!! var clovek5a = new Clovek(); // alert(clovek5a.jazyky); clovek5.jazyky[0] = "JavaScript"; // zmeni pro vsechny instance!! // alert(clovek5a.jazyky); // operator in: // samostatne - zjisteni, zda object ma danou vlastnost // (at v prototypu, nebo v instanci) // alert("jmeno" in clovek5); // true // alert("nejni" in clovek5); // false v cyklu for-in vypisi tzv enumerovatelne vlastnosti (instance i prototypu). Programatorem definovane vlastnosti jsou vzdy enumerovatelne. Neenumerovatelne vlastnosti prototypu (maji priznak [[DontEnum]] se nevypisuji. Ale pokud je zastini enumerovatelna promenna instance, vypisi ji. (Neplati v nekterych verzich IE) // priklad - tostring je NEEnumerovatelna // redefinuji neenumerovatelnou vlastnost clovek5.tostring = function(){return "clovek5";; var msg = "Nema tostring"; for (var p in clovek5){ if (p == "tostring"){ // v IE nechodi msg = "Ma tostring"; break; // alert(msg); // prototyp je mozno zkonstruovat pomoci objektoveho literalu, ale: function Clovek(){ // prazdny konstruktor Clovek.prototype = { id:6, jmeno:"tonda", vypisjmeno:function(){ var clovek6 = new Clovek(); clovek6.vypisjmeno(); // opet vypise hodnoty z prototypu P. Štěpán PHP BI-PHP, výpis 8 3/8
// neni nastavena vlastnost construktor na Clovek!! // novy prototyp je pomoci literalu zkonstruovan jako instance Object! alert(clovek6.constructor == Clovek); // false // alert(clovek6.constructor == Object); // true // potrebuji-li spravnou hodnotu vlastnost construktor, // musim ji do prototypu pridat! function Clovek(){ // prazdny konstruktor Clovek.prototype = { constructor: Clovek, // nastaveni konstruktoru id:6, jmeno:"tonda", vypisjmeno:function(){ clovek6 = new Clovek(); // clovek6.vypisjmeno(); // nyni je nastavena vlastnost construktor na Clovek!! // alert(clovek6.constructor == Clovek); // true prototypy jsou dynamicky svazany s instancemi. Zmeny v prototypu - pridani nove vlastnosti nebo metody do prototypu (pozdeji) se projevi ve vsech instancich (vlastnosti se vyhledavaji podle pointeru). Ale vytvoreni NOVEHO prototypu drive definovane instance nevidi - zustanou v nich pointery na puvodni prototyp!! prototypy maji i nativni typy JavaScriptu (Object, String,...). Tedy i u nich je mozno napr. pridat nove metody: String.prototype.mojeMetoda = function... // nedoporucuje se P. Štěpán PHP BI-PHP, výpis 8 4/8
// kombinace konstruktor a prototyp (umoznuje napr. definovat // parametry konstruktoru) function Clovek(id,jmeno){ this.id = id; this.jmeno = jmeno; this.jazyky = ["Java","PHP"]; Clovek.prototype = { constructor:clovek, vypisjmeno:function(){ // v konstruktoru se obvykle definuji vlastnosti a metody instance, // prototypu metody, sdilene vlastnosti a vlastnost constructor clovek7 = new Clovek(7,"Marie"); // clovek7.vypisjmeno(); // alert(clovek7.id); // dynamicky prototyp - spojeni konstruktoru a prototypu // prototyp se inicializuje uvnitr konstruktoru (pokud neexistuje) function Clovek(id,jmeno){ this.id = id; this.jmeno = jmeno; this.jazyky = ["Java","PHP"]; // metody - provede se jenom pri 1. volani konstruktoru if (typeof this.vypisjmeno!= "function"){ // NUTNO TESTOVAT!! Clovek.prototype = { vypisjmeno:function(){ var clovek8 = new Clovek(8,"Jarka"); // clovek8.vypisjmeno(); // opet nelze redefinovat cely prototyp PO vytvoreni instance!! P. Štěpán PHP BI-PHP, výpis 8 5/8
dedicnost - definuje se pomoci prototypu (retezeni prototypu - prototype chaining) Principielne: prototyp je instanci jineho typu - odkazuje tedy na jiny prototyp. V tom je zase pointer na jeho konstruktor, takze tento konstruktor lze brat jako rodice konstruktoru původního typu function Rodic(){ this.vlastnostrodice = "Rodic"; Rodic.prototype.nactiVlastnostRodice = function(){ return this.vlastnostrodice; function Dedic(){ this.vlastnostdedice = "Dedic"; // definujeme "dedicnost" - prototypu dedice dame odkaz na rodice Dedic.prototype = new Rodic(); // vlastni metoda dedice Dedic.prototype.nactiVlastnostDedice = function(){ return this.vlastnostdedice; var instance = new Dedic(); // alert(instance.nactivlastnostrodice()); // Rodic - zdedena metoda // alert(instance.nactivlastnostdedice()); // Dedic - vlastni metoda // na zacatku retezu prototypu je vzdy jeste jeden - typ Object, // od ktereho dedi vsechny typy (proto ma instance napr. metodu //.tostring) // Zjisteni vztahu instance - konstruktor // alert(instance instanceof Dedic); // true // alert(instance instanceof Rodic); // true // alert(instance instanceof Object);// true // nebo //alert(rodic.prototype.isprototypeof(instance)); // true (atd.) P. Štěpán PHP BI-PHP, výpis 8 6/8
// redefinice nebo vytvoreni novych metod v dedici // nutno pridat do prototypu PO prirazeni: // nelze pouzit objektove literaly - redefinoval by se cely prototyp // nova metoda Dedic.prototype.nactiVelkaZDedice = function(){ return this.vlastnostdedice.touppercase(); // prepsani metody, existujici v rodici Dedic.prototype.nactiVlastnostRodice = function(){ return "Prepsana"; // alert(instance.nactivelkazdedice()); // alert(instance.nactivlastnostrodice()); // opet problemy s referncnimi vlastnostmi apod. // kradeni (maskovani) konstruktoru - umoznuje napr. parametry // konstruktoru // Princip - z konstruktoru dedice se vola konstruktor rodice function Rodic(jmeno){ this.jmeno = jmeno; function Dedic(){ // dedi od Rodic, ale preda parametr Rodic.call(this,"Franta"); // vlastnost instance this.vyska = 185; var instance = new Dedic(); alert(instance.jmeno + "; " + instance.vyska); // problemy: metody museji byt definovany v konstruktoru vzdy znovu // - opet neni mozne opakovane pouziti. Nejsou pristupne metody // rodice, takze vsechny typy museji vyuzivat konstruktor // k definici metod P. Štěpán PHP BI-PHP, výpis 8 7/8
// kombinovana dedicnost (pseudoklasicka dedicnost) - kombinuje // retezeni prototypu (pro dedeni vlastnosti a metod v prototypu) // a kradeni konstruktoru pro dedeni vlastnosti instance. function Rodic(jmeno){ this.jmeno = jmeno; this.jazyky = ["Java","PHP"]; // mohl by byt take parametr Rodic.prototype.zobrazJmeno = function(){ function Dedic(jmeno,vyska){ // zdedene vlastnosti Rodic.call(this,jmeno); this.vyska = vyska; </script> </body> </html> // zdedene metody Dedic.prototype = new Rodic(); // vlastni metoda dedice Dedic.prototype.zobrazVysku = function(){ alert(this.vyska); var instance1 = new Dedic("Jitka",160); var instance2 = new Dedic("Marek",180); instance1.jazyky[0] = "JavaScript"; alert(instance1.jazyky); // "JavaScript","PHP" instance1.zobrazjmeno(); // dedene - Jitka instance1.zobrazvysku(); // 160 alert(instance2.jazyky); // "Java","PHP" instance2.zobrazjmeno(); // dedene instance2.zobrazvysku(); // 180 P. Štěpán PHP BI-PHP, výpis 8 8/8