
Witam ponownie i zapraszam na kolejny odcinek cyklu “przygotowania do MCSD 70-480”. Zgodnie z tytułem, w dzisiejszym wpisie zajmiemy się kwestią obiektów i metod w JavaScript oraz ogólnie tematem programowania obiektowego w tym języku. Tym oto sposobem zamkniemy omawianie pierwszego z czterech głównych tematów jakie należy opanować przed egzaminem - mowa o “Implement and Manipulate Document Structures and Objects” stanowiącym 24% wszystkich pytań podczas testu. Jak zwykle nie ma co przedłużać, sprawdźmy jak przedstawiają się obiekty i metody w języku JavaScript!

Obiekty i metody w języku JavaScript
Witam ponownie i zapraszam na kolejny odcinek cyklu “przygotowania do MCSD 70-480”. Zgodnie z tytułem, w dzisiejszym wpisie zajmiemy się kwestią obiektów i metod w JavaScript oraz ogólnie tematem programowania obiektowego w tym języku. Tym oto sposobem zamkniemy omawianie pierwszego z czterech głównych tematów jakie należy opanować przed egzaminem - mowa o “Implement and Manipulate Document Structures and Objects” stanowiącym 24% wszystkich pytań podczas testu. Jak zwykle nie ma co przedłużać, sprawdźmy jak przedstawiają się obiekty i metody w języku JavaScript!
Obiekty natywne
Język JavaScript dostarcza nam do kilka natywnych/wbudowanych klas, z których możemy korzystać podczas tworzenia skryptów. Poniżej lista najważniejszych z nich (wraz z linkami do w3schools.com zawierającymi ich dokładniejszy opis):
- Number - reprezentuje liczbę
- Boolean - pozwala na konwersję wartości “nie-boolowskich” na boolowskie
- String - umożliwia przechowywanie tekstu i manipulowanie nim
- Array - służy do przechowywania tablic, czyli zmiennych zawierających wiele wartości
- Date - używane jest do pracy z datami
- Math - dostarcza funkcji matematycznych realizujących różnego rodzaju obliczenia
- RegExp - umożliwia tworzenie wyrażeń regularnych
Obiekty takie, są często tworzone w domyśle, tzn. jeśli przypisujemy do jakiejś zmiennej tekst, wówczas tak na prawdę, do zmiennej przypisywany jest obiekt ‘String’, jeśli liczbę to przypisywany jest obiekt ‘Number’ itd. Ponadto każdy z tych obiektów posiada szereg funkcji pozwalających na pracę z danymi, które reprezentują. Poniżej kilka przykładów:
var number = 10; // w tle new Number(10)
var str = 'witaj swiecie'; // w tle new String('...')
var bool = new Boolean(0); // poczatkowa wartość 'false'
var arr = new Array('jeden', 'dwa', 'trzy'); // tablica
var d = new Date(); // data ustawiona na teraz
var regex = new RegExp('witaj', 'g');
alert(str.length); // pobieranie dlugosci tekstu
alert(str.toUpperCase()) // konwersja na wielkie litery
alert(arr.length); // wielkosc tablicy
alert(arr.indexOf('dwa')); // znajdowanie indeksu elementu
d.setDate(d.getDate() + 1); // ustawianie daty
alert(d.getDate()); // pobieranie daty
alert(Math.PI); // wartosc liczby PI
alert(Math.round(1.14567)); // zaokraglanie
alert(str.match(regex)); // uzycie wyrazenia regularnego
Powyższy przykład można sobie przetestować na jsfiddle.net.
To oczywiście tylko mały wycinek dostępnych w tych obiektach funkcji - myślę, że każdy jest w stanie zapoznać się z resztą sam, w miarę potrzeb i nie ma powodu, rozpisywać się na ten temat w tym poście.
Tworzenie własnych obiektów
Podobnie jak w innych językach programowania, wszystkie obiekty, zarówno natywne jak i własne, dziedziczą z obiektu ‘Object’. Samo tworzenie obiektów odbywa się również w sposób powszechnie znany, czyli poprzez użycie słowa kluczowego ‘new’.
W JavaScript nie ma jednak definicji klasy jako takiej. Zamiast tego, można napisać funkcję, którą później wywoła się jak konstruktor, właśnie za pomocą ‘new’ (pisałem już o tym w poprzednim poście). Dla porządku, poniżej przykład:
function SomeConstructor(initialValue) {
this.someValue = initialValue;
}
var someObject = new SomeConstructor('test');
alert(someObject.someValue);
Jak widać, na początku mamy definicję funkcji ‘someConstrutor’, którą w linii piątej wykorzystujemy jako konstruktor tworzący obiekt ‘someObject’.
W tym miejscu możemy przejść od razu do tematu definiowania właściwości obiektów w JavaScript. W przykładzie widać, że w omawianej funkcji mamy przypisanie wartości ‘initialValue’ do zmiennej ‘someValue’ - w tym momencie (pamiętając, że ‘this’ odnosi się do klasy, która wywołuje daną funkcję), w “locie” tworzona jest właściwość, która później dostępna jest w kontekście danego obiektu (patrz linia siódma w przykładzie). Poniżej jeszcze jeden przykład:
var someObject = new Object();
someObject.someValue = 'test';
alert(someObject.someValue);
Kod powyższy pokazuje, że właściwości można tworzyć również po utworzeniu obiektu (linia druga).
Znamy już sposób tworzenia właściwości, czas więc teraz na definicję metody obiektu. Robi się to w sposób analogiczny, z tą różnicą, że zamiast do nowo tworzonej zmiennej obiektu przypisuje się funkcję, a nie konkretną wartość:
function someMethod(someValue) {
alert(someValue);
}
var someObject = new Object();
someObject.methodOne = function () {
alert('witam z metody pierwszej!');
}
someObject.methodTwo = someMethod;
// wywolania
someObject.methodOne();
someObject.methodTwo('witam z metody drugiej!');
W powyższym przykładzie pokazane są dwa sposoby zdefiniowania metody obiektu. Sposób pierwszy - funkcja ‘inline’ (linie od szóstej do ósmej). Sposób drugi - standardowe zdefiniowanie funkcji (linie od pierwszej do trzeciej), a następnie przypisanie jej nazwy do odpowiedniej właściwości obiektu.
Wzorzec modułu
Jak słusznie zauważył w komentarzu do tego posta czytelnik Arek Bal, przy tworzeniu obiektów warto wspomnieć w tym miejscu o wzrorcu modułu.
Dotychczas dowiedzieliśmy się, że do tworzenia obiektów w JavaScript stosuje się funkcje pełniące jednocześnie rolę konstruktorów. Możliwe jest także definiowanie publicznych właściwości i metod już po utworzeniu obiektu (a także wielokrotne ich nadpisywanie). Są jednak sposoby na uzyskanie większej enkapsupalcji czyli odseparowanie właściwości i metod i uczynienie ich prywatnymi. I tutaj właśnie przychodzi nam z pomocą wzorzec modułu. Popatrzmy na taki przykład:
var Module = function (initialValue) {
// prywatna zmienna
var someValue = initialValue;
// prywatna metoda
var calculateValue = function() {
return someValue * 2;
}
return {
// publiczna metoda
getValue : function() {
// uzycie prywatnej metody (nie uzywamy 'this')
return calculateValue() - 1;
}
}
}
var obj = new Module(10);
alert(obj.getValue());
alert(obj.someValue); // zwraca wartosc 'undefined'
alert(obj.calculateValue()); // blad
Przypadek powyższy pokazuje, w jaki sposób za pomocą wzorca modułu można zrealizować enkapsulację zmiennych i metod. W powyższym kodzie, tworzymy zmienną ‘Module’ do której przypisujemy funkcję anonimową - posłuży ona nam później jako konstruktor obiektu. Do zaimplementowania zmiennej i metody prywatnej użyte zostało słowo kluczowe ‘var’ - jak dowiedzieliśmy się we wpisie na temat zakresu zmiennych w JavaScript, tak utworzone zmienne mają zasięg tylko wewnątrz tej funkcji, nie będą więc widoczne z zewnątrz. Nasza funkcja zwraca za to obiekt anonimowy, zawierający właściwość ‘getValue’, do której przypisana zostaje funkcja anonimowa - w ten sposób implementujemy metodę publiczną (tak samo implementuje się też właściwości publiczne).
Prototypy
Dwa akapity wcześniej, wspomniałem o tworzeniu właściwości i metod - dowiedzieliśmy się, że aby je stworzyć, wystarczy dokonać operacji przypisania wartości, a właściwość lub metoda utworzy się w locie. Istnieje jednak jeszcze jedna możliwość…
Każdy obiekt w JavaScript, posiada zdefiniowaną specjalną właściwość zwaną ‘prototype’. Dzięki niej mamy możliwość definiowania prototypów klasy, tzn. możemy zadeklarować zestaw właściwości i metod, które będzie posiadał każdy obiekt (utworzony za pomocą słowa kluczowego “new”). Zobaczmy więc przykład:
function TestClass(value) {
this.someValue = value;
}
// jak wiemy funkcja jest równiez obiektem
// wiec posiada także swój prototyp, a wiec:
testClass.prototype.anotherValue = 'wartosc';
testClass.prototype.method = function() {
alert('funkcja method');
}
// tworzymy obiekt
var obj = new TestClass('test');
// wywolania - wlasciwosc i metoda juz zdefiniowane
alert(obj.anotherValue);
obj.method();
Najpierw oczywiście funkcja będąca też jednocześnie konstruktorem. Następnie sedno sprawy - za pomocą właściwości ‘prototype’ definiujemy właściwość i metodę. Na koniec możemy zaobserwować, że właściwość i metoda są dostępne od razu po utworzeniu obiektu.
Na temat prototypów planowałem już od dłuższego czasu napisać osobnego posta. Myślę więc, że w kontekście egzaminu 70-480, to co napisałem na temat prototypów jest wystarczające, a do tematu jeszcze wrócę w przyszłości.
Znając już zagadnienie prototypów możemy przejść do najważniejszego i jednocześnie ostatniego paragrafu tego wpisu…
Dziedziczenie
Jako że język JavaScript jest w pełni obiektowy, możemy również w pełni korzystać z jego dobrodziejstw, czyli właśnie możliwości dziedziczenia. Niestety realizacja tego zagadnienia jest trochę inna niż w językach takich jak C#. Musimy zaimplementować je sami, przy użyciu właśnie właściwości ‘prototype’.
Spójrzmy najpierw na klasę rodzica:
function Parent() {
// nic nie rób
}
// metody klasy rodzica
Parent.prototype.getValue = function() {
alert('metoda rodzica!!');
}
Parent.prototype.getAnotherValue = function() {
alert('inna metoda rodzica!!');
}
Widzimy, że ma ona dwie metody. W kolejnym przykładzie stworzymy klasę dziedziczącą, która przesłoni pierwszą z nich:
function Child() {
// nic nie rób
}
// dziedziczymy klase parent czyli
// kopiujemy jej prototyp do prototypu dziecka!
Child.prototype = new Parent();
// przeslaniamy metode
Child.prototype.getValue = function() {
alert('wywołano mnie z dziecka!');
}
var obj = new Child();
obj.getValue();
obj.getAnotherValue();
Sprawa wygląda więc całkiem prosto - dziedziczenie polega na przypisaniu do właściwości ‘prototype’ klasy dziedziczącej obiektu klasy rodzica. W ten sposób, wszystkie prototypowe właściwości i metody rodzica, widoczne są również w klasie dziecku. Dziedziczenie takie nazywamy “dziedziczeniem prototypowym”.
Obiekty i metody w języku JavaScript - podsumowanie
To w zasadzie tyle jeśli chodzi o obiekty i metody w języku JavaScript - cały czas zastanawiam się, czy na potrzeby egzaminu 70-480, taki poziom szczegółowości jest wystarczający… Jeśli ktoś z czytelników uważa, że należało by temat poszerzyć, proszę o komentarze, jakich elementów brakuje i co należałoby jeszcze tutaj dopisać! Na pewno rozważę wszystkie propozycje ;)