Strona początkowa

Nowe obiekty

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.

Tworzenie pojedyńczego obiektu

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
	

Tworzenie obiektu za pomocą konstruktora

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
	

Usuwanie właściwości

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;
	

instrukcja Prototype

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
	

Obiekty nadrzędne

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.

Watch Unwatch

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')
    

With

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...

Debugowanie obiektów

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);