Pewnie słyszeliście już nazwę "programowanie obiektowe"? Jeżeli jeszcze nie programowaliście obiektowo, to nie ma się co martwić - JavaScript właściwie cała opiera się na programowaniu obiektowym ;).
W waszych skryptach nieraz i nie dwa odwołujecie się do obiektów. Na przykaład window, document, button itp. to typowe obiekty, które posiadają swoje metody i właściwości :).
Oczywiście jak i inne języki, tak i Javascript umożliwia tworzenie własnych obiektów. Początkowo może się wam wydawać, ze jest to nie za bardzo użyteczne (w końcu można większość rzeczy zrobić przy pomocy funkcji itp), ale gdy zaznajomicie się z obiektami napewno zmienicie zdanie. Przedewzystkim obiekty dają nam ochronę danych. Dziwnie brzmi prada? Na stronie nie raz tworzyłeś button. Każdy z nich ma np. metodę onclick, właściwość value itp. Nie ma żadnego konfliktu w skrypcie, mimo tego, że obiekty te mają metody i właściwości takie same jak ich "bracia". Poza tym programowanie obiektowe jest najbardziej bliskie rzeczywistości. Zapraszam do lektury.
Aby utworzyć nowy pojedyńczy obiekt możemy skorzystamy z poniższej konstrukcji:
var obiekt_1 = {
imie: "Marcin",
wzrost: 184,
wypisz : function() {
alert(this.imie)
}
}
Nasz obiekt już na starcie posiada dwie ustalone właściwości - imie i wzrost, oraz jedną metodę, która wypisuje jego imię. Metoda taka jest zwykłą funkcją utworzoną w konstrukcji obiektu. Dostęp do niej ma tylko ten obiekt. Dzięki temu realizujemy podstawową funkcję obiektowości - dostęp do właściwości i metod ma tylko dany obiekt.
Zwróć uwagę, że każda właściwość i metoda jest od następnej oddzielona przecinkiem (jest on wymagany).
Aby odwołać się do danego obiektu z jego wnętrza stosujemy instrukcję this. Dzięki temu możemy w łatwy sposób wywoływać z wnętrza inne metody danego obiektu lub korzystać z jego właściwości:
var obiekt_1 = {
liczba: 100,
kwadrat : function() {return this.liczba * this.liczba },
wypisz : function() {
alert(this.kwadrat())
}
}
obiekt_1.liczba = 200;
obiekt_1.wypisz();
Gdy nasz obiekt już istnieje, możemy do niego dodawać nowe metody lub właściwości, tak samo jak to robimy normalnie dla obiektu window. Popatrz na kod:
x = 10;
//jest równoznaczne z
window.x = 10;
pisz = function() {...}
//jest równoznaczne z
window.pisz = function() {...}
Tak naprawdę deklarując zwykłe zmienne globalne i funkcje definiujemy po prostu właściwości i metody dla obiektu window. JS nie wymaga stosowania odwołania do window, więc praktycznie zawsze pomijamy to odwołanie.
Podobnie właściwości i metody możemy deklarować dla nowych obiektów:
var obiekt_1 = {
imie: "Marcin",
wzrost: 184,
wypisz : function() {
alert(this.imie)
}
}
obiekt_1.waga = 73; //dodaliśmy nową właściwość
obiekt_1.wypisz = function() {alert('Wzrost: '+this.wzrost + ', waga: '+this.waga)} //dodaliśmy nową metodę
obiekt_1.wypisz();//zwróci Wzrost: 184, waga: 73
Powiedzmy, że zamiast pojedyńczego obiektu chcemy utworzyć ich kilka. Każdy obiekt powinien posiadać jakieś właściwości i metody np. width, height i wypisz().
Aby utworzyć kilka podobnych obiektów posłużymy się klasą obiektu. Czym jest klasa? To rodzaj foremki, która opisuje nam jak będą wyglądać tworzone na jej podstawie nowe obiekty. W JS klasę tworzymy za pomocą zwykłej funkcji:
function _KlasaObiektu(_width,_height) {
this.width = _width;
this.height = _height;
this.wypisz = function() {
alert(this.width + 'x' + this.height)
}
}
Od tej pory każdy utworzony obiekt na podstawie _KlasaObiektu() będzie miał ustawione właściwości width i height na podane w atrybutach, oraz metodę wypisz. Specyficzna nazwa tej klasy czyli zaczynająca się od _ i dużej litery to tylko mój estetyczny wymysł, by łatwo oddzielić co pełni rolę klasy, a co funkcji.
Aby utworzyć nowy obiekt na podstawie istniejącej klasy stosujemy instrukcję new:
var obiekt_1 = new _KlasaObiektu(200,100); var obiekt_2 = new _KlasaObiektu(300,200); obiekt_1.wypisz(); //wypisze 200x100 obiekt_2.width = 600; obiekt_2.wypisz()//wypisze 600x200
Aby usunąć właściwość obiektu, skorzystamy z operatora delete:
var kubek = {
kolor: 'zielony',
wielkosc: 'duzy',
cena: 'wielka'
}
kubek.cena = 'mala';
//Usuwamy właściwość cena
delete kubek.cena;
Javascript udostępnia jeszcze jeden sposób deklarowania metod i właściwości dla klas obiektu. Skorzystamy tutaj z instrukcji prototype.
function _KlasaObiektu() {
this.imie = 'Marcin';
this.wzrost = 183
}
_KlasaObiektu.prototype.pisz = function () {//funkcja która będzie metodą naszego obiektu
document.write(this.imie + ' ma ' + this.wzrost + 'cm wzrostu.')
}
_KlasaObiektu.prototype.waga = 73;
var obiekt_1 = new _KlasaObiektu();
obiekt_1.pisz();
document.write(obiekt_1.waga)//wypisze sie 73
W kodzie klasy nie deklarowaliśmy żadnych metod i właściwości. Dopiero korzystając z intrukcji prototype dodaliśmy metodę pisz i właściwosć waga do naszej klasy. Od tej pory każdy nowo utworzony obiekt będzie posiadał te dodatkowe właściwości i metody.
Instrukcja prototype przydaje się, gdy do danego obiektu np String czy Array chcemy dodać dodatkową funkcjonalność np:
function mixuj() {
var tekst = '';
for (var x=0; x<this.length; x++) {
tekst += (x%2==0)?this.charAt(x).toUpperCase() : this.charAt(x).toLowerCase()
}
return tekst;
}
String.prototype.pierwszaDuza = function() {return this.charAt(0) + this.substr(1);}
String.prototype.mix = mixuj;
Zadeklarowaliśmy dwie metody dla klasy obiektu String korzystając z dwóch różnych sposobów deklaracji funkcji (jedna jest funkcją anonimową). Od tej pory każdy nowy string (tekst) będzie miał te metody, dzięki której zamieni piewszą literę na dużą oraz zmiksuje wielkość liter:
var tekst = "marcinek"; alert(tekst.pierwszaDuza()) //wypisze Marcinek var tekst = "marcinek"; alert(tekst.mix()) //wypisze mArCiNeK
Przypuśćmy, że mamy obiekt w obiekcie:
function _KlasaObiektu() {
this.imie = 'Marcin';
this.wzrost = 183
this.guzik = document.createElement('input');
this.guzik.onclick = function() {
this.value = ???
}
document.getElementsByTagName('body')[0].appendChild(this.guzik);
}
Utworzyliśmy klasę obiektu, który po stworzeniu będzie tworzył obiekt guzika. Po kliknięciu na ten guzik, jego value powinno przyjąć wartość wzrosu. Jak jednak to zrobić, skoro instrukcja this wewnątrz obiektu guzika wskazuje na niego samego?
Niestety Javascript nie obsługuje takiego odwoływania. Jest na to jednak sposób. Wystarczy utworzyć zmienną, wkazującą na dany obiekt, a potem wykorzystać ją przy odwoływaniu z obiektów podrzędnych:
function _KlasaObiektu() {
this.ob = this;
this.imie = 'Marcin';
this.wzrost = 183
this.guzik = document.createElement('input');
this.guzik.onclick = function() {
var o = this.ob;
this.value = o.wzrost
}
document.getElementsByTagName('body')[0].appendChild(this.guzik);
}
Jesto to bardzo ważna technika - do zapamiętania albo do wytatuowania na czole. Będziemy często ją stosowali pisząc obiektowo w Javascripcie.
Javascript udostępnia nam metodę watch("nazwa_wlasciwosci", funkcja(id, stara_wartosc, nowa_wartosc), która służy do podglądu właściwości obiektów. Jej działanie jest takie samo jak w innych językach. Gdy podglądana wartość się zmieni, wówczas watch wywoła funkcję podaną w drugim atrybucie. Przekazujemy jej 2 parametry - pierwszy określa nazwę obserwowanej właściwości, drugi to funkcja z 3 atrybutami: id, staraWartosc, nowaWartosc. Parametry te są wypełniane automatycznie. Gdy obserwowana wartość się zmieni, funkcja podana w 2 atrybucie zostanie wywołana:
obiekt = {p:1}
//śledzenie właściwości "p" obiektu "obiekt"
obiekt.watch("p",
function (id, staraWartosc, nowaWartosc) {
console.log("o." + id + " zmienilo sie z "
+ staraWartosc + " na " + nowaWartosc)
return nowaWart;
})
//teraz przy każdej zmianie wartości obiektu wywołany zostanie callback
//który śledzi zmiany w obiekcie
obiekt.p = 2
obiekt.p = 3
Aby zakończyć obserwowanie, korzystamy z metody unwatch("nazwa_obserwowanej_wlasciwości").
obiekt.unwatch('p')
Ciekawym sposobem odwoływania się do właściwości jest stosowanie instrukcji with (przydaje się to także w innych syuacjach gdzie częstokroć powatarzamy właściwość danego obiektu):
function klasaObiektu() {
this.imie = 'Marcin';
this.wzrost = 183
this.waga = 73
this.wlosy = 'czarne'
this.oczy = 'bronzowe'
this.pisz = function() {
with (this) {
var tekst = 'imie: ' +imie+ "\n"
tekst += 'wzrost: ' +wzrost+ "\n"
tekst += 'waga: ' +waga+ "\n"
tekst += 'wlosy: ' +wlosy+ "\n"
tekst += 'oczy: ' +oczy;
return tekst;
}
}
}
Niektórzy nie zalecają stosowania tej instrukcji. Osobiście bardzo żadko korzystam z jej doprodziejstw, jednak uważam, że kod napisany z jej pomocą jest całkiem czytelny. Kto co lubi...
Czasami będziemy chcieli debugować używane obiekty. Możemy wtedy użyć metody toSource(), która konwertuje obiekt do postaci jego źródła (tekstu), oraz właściwości constructor, która jest dostepna dla każdego obiektu, i która wskazuje na konstruktor obiektu:
function _Klasa(_speed, _kolor) {
this.speed = _speed;
this.kolor = _kolor;
}
var obj = new _Klasa(10, "czerwony");
console.log(obj.constructor);
console.log(obj.toSource());
Łatwo możemy napisać funkcję, która będzie wypisywać informacje o obiekcie:
function debug(obj) {
var w = obj.toSource(); //zrodlo obiektu
w += 'Konstruktor : ' + obj.constructor; //konstruktor obiektu
console.log(w);
}
var obj = new _Klasa(10, "czerwony");
debug(obj);