Tworzymy strony WWW bo lubimy :)

10 wrz 2009

JavaScript - programowanie obiektowe, część 1

Opublikowany w Pozostałe aktualności, Ext JS

Jak podaje Wikipedia JavaScript to obiektowy skryptowy język programowania stworzony przez firmę Netscape. Nie wdając się na samym początku w szczegóły można powiedzieć, że w JavaScript wszystko jest obiektem.

Obiekt można zdefiniować jako unikalny zbiór zapisanych danych i operacji pracujących na tych danych. Obiekty z kolei mogą być pogrupowane w klasy. W JavaScript obiekty są tworzone operatorem new, po którym następuje nazwa klasy, której egzemplarz ma zostać utworzony.

W tym miejscu trzeba podkreślić, że JavaScript ma szereg wbudowanych klas oraz, że my sami także możemy tworzyć własne klasy. Przykładami wbudowanych klas są: Object, Boolean, Number, String, Function, Array, Date.

I tak np. zapis w postaci:

var x = new Object();

oznacza, że został utworzony egzemplarz x klasy Object, która jest wbudowaną klasą JavaScript. Równie dobrze możemy tworzyć swoje własne klasy, ale o tym będzie za chwilę.

Klasa Object jest klasą bazową, której potomkami są wszystkie inne klasy. Wszystkie właściwości i metody klasy Object są obecne również w innych klasach. Z tego powody poznanie klasy Object ułatwia zrozumienie działania innych klas.

Klasa Object posiada dwie właściwości:

constructor – referencja do funkcji, która stworzyła obiekt. W klasie Object wskazuje na wewnętrzną funkcję Object();

prototype – referencja do prototypu bieżącego obiektu. Dzięki prototype można dodawać nowe właściwości i metody do istniejących klas poprzez dodanie im prototypu funkcji powiązanego z konstruktorem funkcji dla tych klas.

Aby lepiej zrozumieć czym są i jak działają powyższe właściwości zdefiniujemy własną klasę (która de facto w naszym przykładzie jest egzemplarzem klasy Object).

var nowaKlasa = new Object();
nowaKlasa.miasto = "Warszawa";
nowaKlasa.kraj = "Polska";
nowaKlasa.pokaz = function() {
	alert(this.miasto);
	}
			
nowaKlasa.pokaz(); // wyświetli "Warszawa"
alert(nowaKlasa.kraj); // wyświetli "Polska"

W powyższym fragmencie kodu utworzyliśmy klasę o nazwie nowaKlasa i dynamicznie przypisaliśmy jej 2 właściwości (miasto, kraj) oraz jedną metodę pokaz. W metodzie pokaz użyliśmy słowa kluczowego this. Słowo this zawsze wskazuje na obiekt, który wywołuje daną metodę. Zatem fragment kodu:

var nowaKlasa = new Object();
nowaKlasa.pokaz = function() {
	alert(this.miasto);
	}

oznacza to samo co zapis w formie:

var nowaKlasa = new Object();
nowaKlasa.pokaz = function() {
	alert(nowaKlasa.miasto);
	}

Jako, że właściwości naszej klasy (nowaKlasa) utworzyliśmy dynamicznie bez użycia konstruktora i prototypu, wobec czego właściwości klasy bazowej Object (constructor, prototype) na niewiele nam się zdadzą. Aby zademonstrować ich działanie potrzebny nam będzie bardziej rozbudowany przykład.

function samochod(kolor, drzwi) {
	this.kolor = kolor;
	this.drzwi = drzwi;
this.kierowcy = new Array("Łukasz", "Urszula");
}
			
samochod.prototype.pokazKolor = function() {
	alert(this.kolor);
	}
			
var pojazd1 = new samochod("czerwony", 4);
var pojazd2 = new samochod("niebieski", 3);
			
pojazd1.kierowcy.push("Andrzej");
			
alert(pojazd1.kierowcy); // wyświetli "Łukasz,Urszula,Andrzej"
alert(pojazd2.kierowcy); // wyświetli "Łukasz,Urszula"
			
alert(pojazd1.constructor); // wyświetli źródło konstruktora, który w naszym wypadku jest funkcja samochod()

Powyższy sposób definiowania klas jest przez programistów czasami określany jako „hybrydowy wzorzec konstruktor-prototyp” (ang. hybrid constructor-prototype paradigm).

Czym zatem różni się ten sposób definiowania klas od poprzedniego widocznego na listingu 1? Główna różnica jest taka, że możemy tworzyć wiele egzemplarzy tej samej klasy i wypełniać je różnymi wartościami. Klasę samochod tworzymy za pomocą konstruktora, który jest niczym innym jak zwykłą funkcją, która może, ale nie musi posiadać argumenty. W konstruktorze umieszczamy wszystkie właściwości danej klasy (w naszym przypadku są to: kolor, drzwi, kierowcy). Natomiast metody klasy (czyli funkcje, w naszym wypadku jest to pokazKolor) umieszczamy na zewnątrz poza konstruktorem wykorzystując właściwość prototype.

Utworzyliśmy zatem własną klasę samochod i jej dwa egzemplarze, pojazd1 i pojazd2, które są od siebie niezależne, co zresztą widać gdy dodajemy imię „Andrzej” do tablicy kierowcy w egzemplarzu klasy pojazd1. Ostatnia linijka kodu pokazuje sposób działania właściwości constructor.

Oprócz dwóch właściwości (constructor, prototype) klasa Object posiada także kilka metod.

hasOwnProperty(właściwość) – sprawdza czy obiekt posiada daną właściwość, np.

var o = new Object;
o.nazwisko = "Kowalski";
alert(o.hasOwnProperty("nazwisko")); // wyświetli true
alert(o.hasOwnProperty("imie")); // wyświetli false

isPrototypeOf(obiekt) – sprawdza czy obiekt jest prototypem innego obiektu, np.

function pytanie(){
      this.odpowiedz = 42;
}
            
function drugiePytanie() {}
            
drugiePytanie.prototype = new pytanie();
var mojePytanie = new drugiePytanie();
            
alert(pytanie.prototype.isPrototypeOf(mojePytanie)); // wyświetli true

propertyIsEnumerable(właściwość) – sprawdza, czy dana właściwość może zostać wyliczona za pomocą instrukcji for … in, np.

var a = new Object;
a.kolor = "czerwony";
a.waga = 200;
a.kraj = new Array("Polska", "Niemcy", "Francja");
		   
alert(a.propertyIsEnumerable("kraj")); // wyświetli true
		   
for(wlasciwosc in a.kraj) {
alert(a.kraj[wlasciwosc]); // wyświetli po kolei: Polska, Niemcy i Francja
}

Przy okazji omawiania instrukcji forin warto wiedzieć, że za jej pomocą możemy wyświetlić wszystkie właściwości i metody każdego obiektu, zarówno wbudowanego w język JavaScript jak i utworzonego przez nas samych, np.

var property = "";
for(prop in location) {
property += prop + "
";
}
alert(property); // wyświetli wszystkie właściwości objektu location

toString() – zwraca reprezentację obiektu w postaci zwykłego ciągu tekstowego.

valueOf() – zwraca najbardziej odpowiednią wartość prostą dla obiektu.

W tym miejscu warto wspomnieć o dwóch operatorach, typeof oraz instanceof. Operator typeof zwraca "object" bez względu na jaki typ obiektu wskazuje, natomiast operator instanceof sprawdza czy obiekt jest konkretnego typu i zwraca "true" lub "false", np.

var stringObjekt = new String("jakiś tekst");
alert(typeof stringObjekt); // wyświetli "object"
alert(stringObjekt instanceof String); // wyświetli true
alert(stringObjekt instanceof Array); // wyświetli false

Łukasz Różewicz