Wykład 4 Programowanie obiektowe
Tworzenie własnych obiektów Słowo kluczowe this W JavaScript można tworzyć własne obiekty. Wykorzystuje się tu zapis utworzonej funkcji o nazwie takiej samej jak klasa, dla której definiujemy właściwości (zmienne proste lub obiektowe) i metody (funkcje wewnętrzne). Nadając i wykorzystując identyfikatory właściwości i metod korzystamy ze słowa kluczowego this. Rozumiane jest ono jako obiekt bieżący, właściciel metody lub właściwości. Poniżej prosty przykład:
AKAPIT function kolor1( ) { this.document.bgColor = "red"; } function kolor2(e) { e.style.color = "yellow"; } Przykład
W powyższym przykładzie mamy dwie własne funkcje. W pierwszej o nazwie kolor1 ustalamy kolor tła dla bieżącego obiektu (this) - którym jest domyślnie window. Druga funkcja o nazwie kolor2 zmienia kolor czcionki przekazanego do funkcji obiektu e - wywoływana jest kliknięciem w przycisk z przekazaniem odniesienia do tego właśnie przycisku (this), a więc zmienia się kolor napisu na przycisku.
var piesek1 = { imie: "Maja", rasa: "kundel", kolor: "zółty", plec: "suczka", glos:"haaaauuuu", dajGlos: function () { document.writeln(this.glos); } }; document.writeln(piesek1.imie); piesek1.dajGlos (); Najmniej uniwersalny sposób - tworzenie konkretnej instancji (egzemplarza) bez definicji:
function Nazwa (argument1,argument2, itd) { this. właściwość1 = argument1; this. właściwość2 = argument2; this.metoda= function( argumenty) {opis metody} //inne właściwości i inne metody } Definicja konstruktora obiektu - Metoda 1 Można utworzyć definicję – tzw. konstruktor obiektu (klasa) wykorzystując własną funkcję:
Teraz tworzymy egzemplarze (instancje) obiektu i przypisujemy do zmiennych referencji p1 i p2: var p1 = new Nazwa( argumenty aktualne); var p2 = new Nazwa( inne argumenty aktualne);
//funkcja konstruktor function Pies(imie, rasa, kolor, plec) { this. imie = imie; this. rasa = rasa; this. kolor = kolor; this. plec = plec; this.glos ="HAUUUU"; //dla wszystkich psów ten sam this.dajGlos = function () { document.writeln (this.glos); }; //tworzymy instancje var piesek1 = new Pies( "Misia", "Labrador", "czekoladowa", "suka" ); var piesek2 = new Pies( "Misiek", "Seter", "rudy", "pies" ); //możemy mieć dostęp do właściwości i metod document.writeln(piesek1.imie); // ->wypisze Misia piesek1.dajGlos (); // -> wykonanie metody - metoda wypisze "HAUUUU" //itd. Przykład właściwości przykładowa metoda
//funkcja konstruktor function Pies(imie, rasa, kolor, plec) { this. imie = imie; this. rasa = rasa; this. kolor = kolor; this. plec = plec; this.glos ="HAUUUU"; //dla wszystkich psów ten sam this.dajGlos = function () { return this.glos; }; //tworzymy instancje var piesek1 = new Pies( "Misia", "Labrador", "czekoladowa", "suka" ); var piesek2 = new Pies( "Misiek", "Seter", "rudy", "pies" ); //możemy mieć dostęp do właściwości i metod document.writeln(piesek1.imie); // -> Misia document.writeln(piesek1.dajGlos ()); // -> instrukcja wypisuje wynik metody //itd. Przykład – metoda typu "get" właściwości metoda "get" (pobierz)
//funkcja konstruktor function Pies(imie, rasa, plec) { this. imie = imie; this. rasa = rasa; this. kolor = "";//pusta wartość this. plec = plec; this.glos ="HAUUUU"; //dla wszystkich psów ten sam this.ustawKolor = function (k ) { this.kolor = k; }; //tworzymy instancje var piesek1 = new Pies( "Misia", "Labrador", "czekoladowa", "suka" ); var piesek2 = new Pies( "Misiek", "Seter", "rudy", "pies" ); //możemy mieć dostęp do właściwości i metod document.writeln("Kolor pieska 1:",piesek1.kolor); // -> jeszcze nie ma piesek1.ustawKolor("kremowa"); // -> instrukcja nadaje wartość właściwości document.writeln("Kolor pieska 1:",piesek1.kolor); // -> teraz kremowy Przykład – metoda typu "set" właściwości metoda "set" (ustaw)
Metody typu "get" zwracają jakąś wartość. W opisie metody jest zapis: return co_zwraca_metoda To co zwraca metoda można potem wypisać na ekranie: document.write(obiekt.metoda(argumenty)) lub użyć w wyrażeniach zgodnie z typem zwracanej wartości Metody typu get najczęściej zwracają właściwość obiektu - jest to tzw. hermetyzacja – dostęp do właściwości powinien być tylko przy użyciu metod Metody typu "set" ustawiają wartość właściwości obiektu lub ją zmieniają – nie maja return. Wykorzystuje się je jako osobną instrukcję w postaci: obiekt.metoda(argumenty)
function Auto (nr,nazwa) { //właściwości this.nr=nr; this.nazwa = nazwa; this.predkosc=0; //metody this.podajNazwe = function(){ return this.nazwa; } this.przyspiesz = function(dp){ this.predkosc=dp; } this.zatrzymaj = function(){ this.predkosc=0; } this.pokazPredkosc = function(){ return this.predkosc; } this.wyswietl = function(){ var d=document.getElementById('ed'+this.nr); d.innerHTML=this.podajNazwe()+' ma prędkość:'+this.pokazPredkosc(); } } Inny przykład konstruktora obiektów wybór elementu do wyświetlenia wartości
//funkcja konstruktor Pies2 = function (imie, rasa, kolor, plec) {//definiowanie instancji obiektu – tu uwaga na "interpunkcję"! var obiektPies = { imie: imie, rasa: rasa, kolor: kolor, plec: plec, glos:"HAUUU", dajGlos: function ( ) { document.writeln(this.glos); } }; //funkcja zwraca zdefiniowany obiekt return obiektPies; };// KONIEC FUNKCJI //tworzenie instancji klasy var piesek3 = new Pies2( "Milka", "Spaniel", "czekoladowa", "suka" ); var piesek4 = new Pies2( "Reksio", "Dog", "czarny", "pies" ); //możemy mieć dostęp do właściwości i metod document.writeln(piesek3.imie); piesek3.dajGlos (); //itd. Sposób 2 –wykorzystuje tworzenie instancji
W obu sposobach mamy ogólne definicje konstruktorów obiektu. Potem możemy tworzyć wiele egzemplarzy różniących się wartościami właściwości i wykonywać metody (akcje).
Ograniczenia dostępu do pól JakisObiekt= function (n1,n2) { w_pryw= n1; //właściwość prywatna (nie ma this) this.w_pub=n2; //właściwość publiczna this.podajNazwe1 = function () {return w_pryw; }; this.podajNazwe2 = function () {return this.w_pub; }; }; t = new JakisObiekt("prywatna","publiczna"); document.writeln("met1:",t.podajNazwe1());//tylko metoda podaje właściwość prywatną document.writeln("met2:",t.podajNazwe2());//metoda podaje też właściwość publiczną document.writeln("w_pryw:",t.w_pryw);//nie ma dostępu do prywatnej - undefined document.writeln("w_pub:",t.w_pub);// jest dostęp do właściwości publicznej
W przypadku konieczności uwzględnienia pewnej liczby elementów o identycznym zestawie właściwości, wchodzących w skład definiowanego obiektu, możemy jedną z właściwości obiektu głównego zdefiniować jako pustą tablicę, a następnie wykorzystać odpowiednią metodę do wypełniania tablicy. Elementami takiej tablicy mogą także być obiekty (mamy wówczas możliwość zróżnicowania typów składowych obiektu). Tablica elementów może być dynamicznie wypełniana w programie (dowolna liczba jej składowych) Obiekty wewnętrzne
var Mechanizm= function(nr, typ) { this.nrR=nr; this.typR= typ; //pusta tablica this.dzwignie=[ ]; this.tworzDzwignie=function(nrDz, dlDz) { //umieść obiekt wewnętrzny w podanej komórce tablicy this.dzwignie[nrDz-1]={ nrD: nrDz, dlD: dlDz, } } //koniec definicji konstruktora //tworzymy egzemplarz obiektu m1=new Mechanizm (1,'x25'); //tworzymy składowe obiektu m1.tworzDzwignie (1, 500); m1.tworzDzwignie (2,600); m1.tworzDzwignie (3,400); //wypisujemy nadane właściwości document.writeln("Robot nr ",m1.nrR, " Typ:",m1.typR); document.writeln("Ręce"); //elementy tablicy dzwignie for (k=0;k<m1.dzwignie.length;k++) { document.writeln("Nr dzwigni:", m1.dzwignie[k].nrD," długość dźwigni:",m1.dzwignie[k].dlD); } metoda właściwości
Oddzialywanie obiektów - gryzie ogon var Kot=function(imie, ogon) { this.imie=imie; this.ogon=ogon; this.ugryz=function(ob){//argumentem metody jest inny obiekt ob.ogon-=5; } kot1= new Kot ("Maciuś",20); kot2= new Kot ("Misiek", 20); document.writeln(kot1.imie," ma ogon ",kot1.ogon); document.writeln(kot2.imie," ma ogon ",kot2.ogon); document.writeln(kot2.imie," gryzie ", kot1.imie); kot2.ugryz(kot1); //kot2 gryzie kota1 document.writeln(kot1.imie," ma teraz ogon ",kot1.ogon); document.writeln(kot1.imie," gryzie ", kot2.imie); kot1.ugryz(kot2); //kot1 gryzie kota2 document.writeln(kot2.imie," ma teraz ogon ",kot2.ogon); Oddziaływanie wzajemne obiektów
Prototyp (prototype) to specjalna właściwość obiektowa, którą posiada każdy obiekt. Prototyp można wykorzystać do nadania dowolnej wartości (obiektowej). Prototyp oznacza obiekt, który niejawnie udostępnia swoje właściwości innym obiektom. W języku JavaScript prototyp jest tworzony oraz udostępniany każdej powstającej instancji wybranej klasy. Gdy użytkownik pragnie odczytać właściwość, która nie jest obecna w danej instancji, jest ona wyszukiwana w jej prototypie. Wykorzystamy prototyp do przepisania całego obiektu nadrzędnego do obiektu podrzędnego i utworzenia nowych właściwości obiektu potomnego. Dziedziczenie obiektów
//konstruktor przodka var Zwierze = function(wiek) { this.nazwaPrzodka ='zwierzę'; this.wiek = wiek; } //konstruktor potomka var Kot = function(imie, siersc) { this.nazwaKlasy='Kot'; this.nazwa = imie; this.nogi = 4; this.siersc= siersc; } //przepisanie do prototypu właściwości obiektu Zwierze( ) Kot.prototype = new Zwierze(4) //nowa właściwość prototypu Kot.prototype.glos = 'Miauuu'; //tworzymy obiekt k= new Kot("Maciek", "ruda"); //wyświetlamy jego właściwości document.writeln("Utworzyliśmy obiekt typu:",k.nazwaKlasy) ;// wyświetli: Kot document.writeln("Przodek:",k.nazwaPrzodka); // wyświetli: zwierzę document.writeln("Nazwa:",k.nazwa) ;// wyświetli: Maciek document.writeln("Nogi:",k.nogi) ;// wyświetli: 4 document.writeln("Głos:",k.glos) ;// wyszukane w prototypie - wyświetli: Miauuu document.writeln("Wiek:",k.wiek) ;// wyświetli: 1 document.writeln("Sierść:",k.siersc) ;// wyświetli: ruda
//konstruktor przodka urzElektr = function(napiecie, producent) { this.nazwa ='Urządzenie elektryczne'; this.napiecie = napiecie; this.producent = producent; } //konstruktor potomka Lodowka = function(producent, temp_min) { this.nazwaKlasy='Lodówka'; this.temp_min=temp_min; } //przepisanie do prototypu właściwości obiektu urzElektr( ) Lodowka.prototype = new urzElektr(230, "Bosch") //możemy dodać nową właściwość prototypu Lodowka.prototype.podtyp= "AGD" ; //tworzymy obiekt k= new Lodowka( -10); //wyświetlamy jego właściwości document.writeln("Utworzyliśmy obiekt typu:", k.nazwaKlasy) ;// wyświetli: Lodówka document.writeln("Przodek:", k.nazwa); // wyświetli: Urządzenie elektryczne document.writeln("Producent", k.producent) ;// wyświetli: Bosch document.writeln("Podtyp:",k.podtyp) ;// wyszukane w prototypie - wyświetli: AGD
UWAGA: Przy tworzeniu hierarchii obiektów należy uważnie i sensownie projektować właściwości i metody. W powyższym przykładzie producent i napięcie są właściwościami urzElektr() (każde urządzenie elektryczne producenta, napięcie), zaś temperatura minimalna, podtyp to już właściwości lodówki (nie każde urządzenie ma podtyp czy temperaturę jako ważną właściwość).
Przedefiniowanie metody obiektu nadrzędnego w obiekcie potomnym nosi nazwę polimorfizmu (wielopostaciowości). Polimorfizm (wielopostaciowość)
// konstruktor obiektu Auto( ) function Auto (x) { this.nazwa = x; this.podajNazwe = function( ){ document.writeln("Auto:", this.nazwa); } // konstruktor obiektu AutoSuper function AutoSuper(n) { //przepisanie do prototypu cech obiektu Auto( ) this.prototype = new Auto(n); //nowa właściwość this.prototype.turbo=" Ma turbo"; //można nadpisać (przedefiniować) metodę podajNazwe AutoSuper.prototype.podajNazwe = function ( ) { document.writeln("Auto-super:",this.prototype.nazwa," turbo:", this.prototype.turbo); }; }
Teraz mamy: Definicję obiektu Auto() – z metodą podajNazwe() Definicję obiektu potomnego AutoSuper() posiadającą: o dodatkową właściwość turbo o przedefiniowaną metodę podajNazwe(). //tworzymy instancję obiektu Auto( ) var a1 = new Auto("MacLaren"); //wywołujemy funkcje utworzonego obiektu a1.podajNazwe( ); // sprawdzamy istnienie właściwości turbo if (a1.turbo==undefined) document.writeln("To auto nie ma turbo"); //inicjalizujemy obiekt potomny var a2 = new AutoSuper("MacLaren"); // sprawdzamy przedefiniowanie metody podajNazwe() a2.podajNazwe( );