Javascript ir OOP
5 (100%) 1 vote

Javascript ir OOP

Objektai

JavaScript kalboje sutiksite begalę objektų. Masyvai yra objektai. Funkcijos yra objektai. Objektai yra objektai. Tačiau kas yra objektai iš tiesų? JavaScript objektai – tai vardo ir reikšmės sąrašai. Vardai yra simbolių sekos, o reikšmės gali būti skaičiai, simbolių sekos, loginės reikšmės bei objektai (taip pat masyvai ir funkcijos). JavaScript objektai naršyklėse paprastai įgyvendinami sąrašų (hashtable)pagalba, kad reikšmės galėtų būti gražinamos kuo greičiau.
Jeigu reikšmė vardo-reikšmės sąraše yra funkcija, galime laikyti, kad tai metodas. Kai iškviečiamas objekto metodas, metodo kintamasis this laiko nuorodą į objektą. Per šį kintamąjį metodas turi priėjimą kitų jį iškvietusio objekto metodų.
Visi objektai yra kuriami per konstruktorių – funkciją, kuri parengia objektą darbui. Konstruktoriai JavaScript kalboje suteikia tokias galimybes, kokias kitose kalbose suteikia klasės (taip pat statinius laukus ir metodus).
Ar JavaScript yra objektiškai orientuota programavimo kalba (OOP)?
JavaScript objektai laiko duomenis bei metodus, kurie jais operuoja. JavaScript objektai gali susidėti iš kitų objektų.
JavaScript neturi klasės (class) raktažodžio savo sintaksėje, tačiau palaiko konstruktoriaus metodus, kurie atlieka tai, ką klasės daro kitose OOP kalbose: saugo objekto duomenis ir metodus.
JavaScript neturi tiesioginio klasių paveldėjimo, bet paveldimumas įgyvendinamas naudojant prototipus.
Du pagrindiniai būdai kurti objektų struktūras OOP kalbose yra paveldėjimas (ryšys: yra) ir agregacija (ryšys: turi). JavaScript ne tik palaiko juos abu. Tuo tarpu dinamiška kalbos prigimtis, leidžia naudoti agregaciją ir po klasės apibrėžimo. Tai leidžia objektų struktūrą dinamiškai keisti bet kuriuo programos vykdymo momentu (kas visiškai neįmanoma statinėse OOP kalbose).
Kartais sakoma, kad JavaScript nėra visiškai objektiškai orientuota, nes ji negali maskuoti objektų informacijos. T.y. objektai neturi privačių (private) laukų bei metodų. Visi objekto nariai yra vieši (public).
Tačiau iš tiesų JavaScript gali palaikyti privačius laukus ir metodus. Žinoma, nedaugelis tą supranta ir išnaudoja, nes JavaScript yra viena menkiausiai išaiškintų programavimo kalbų.
Kartais argumentuojama, kad JavaScript nėra pilnai objektiškai orientuota, nes nepalaiko paveldėjimo. Bet kaip vėliau paaiškėja, JavaScript ne tik palaiko klasikinį paveldėjimą (prototipų strategija), bet ir daugelį kitų daugkartinio kodo panaudojamumo šablonų.
Pateiksime kelis šių faktų įrodymus ir išsiaiškinsime kaip jie veikia.

Ar JavaScript palaiko agregaciją.

Turime dvi paprastas JavaScript „klases“ (funkcijas). Pirmoji klasė lenktynininkas (Racer), aprašo objektą, kuris turi vardą (name). Laikantis OOP principų, vardui gražinti ir įrašyti naudosime getName ir setName metodus. Mūsų lenktynininkas taip pat moka trumpai prisistatyti metodu Hello.

function Racer() { }
Racer.prototype = {
  name: null,
  getName: function() { return this.name },
  setName: function(name) { this.name = name },
  Hello: function() { alert("Hi, I`m your driver, " + this.name + "! Let`s take a ride!") }
};

Kaip minėta anksčiau, objekto laukai ir metodai aprašomi naudojant prototipų paveldimumą. T.y. tuščia „klasė“ Racer praplečiama lauku name ir metodais getName, setName bei Hello per objekto prototype lauką.

Antroji klasė (Car) aprašo automobilį ir turi du laukus: name (automobilio pavadinimas) ir driver (vairuotojas). Bei metodą Hello (prisistatymui). Atkreipkite dėmesį, nors žinoma, kad automobilio vairuotojas greičiausiai bus Racer objektas, tai niekaip nenurodoma programos kode. Taip yra todėl, kad JavaScript yra silpnai tipizuota kalba ir objektas gali keisti tipą dinamiškai (pvz. atliekant priskyrimo veiksmą). Tačiau, tai dar nereiškia, kad objekto tipo negalima identifikuoti.

function Car(name) {
  this.name = name;
}
Car.prototype = {
  driver: null,
  setDriver: function() { return this.driver },
  setDriver: function(driver) { this.driver = driver },
  Hello: function(){ 
    alert("I am, " + this.name + " weels driven by " + this.driver.getName() + "."); 
  }
};

Turbūt pastebėjote, kad automobilio pavadinimas (name) perduodamas per konstruktorių (Car). Tokia formuluotė leis greičiau parengti Car objektą darbui. Tuo tarpu metodu setDriver bus nurodoma, kas vairuoja automobilį.

Šiame kode panaudojamos abi klasės Racer ir Car.

var me = new Racer();
me.setName("Max");
var car = new Car("Suzuki");
car.setDriver(me);
car.driver.setName("Mr. Max");
car.Hello();
car.driver.Hello();

Pirma sukuriame asmenį me. Pavadiname jį Max. Sukuriame automobilį bei pavadiname jį Suzuki. Nurodome, kad automobilį vairuos ponas Max (objektas me) ir pervadiname mūsų vairuotoją į (Mr. Max). Pabaigoje leidžiame prisistatyti automobiliui (car.Hello()) ir jo vairuotojui (car.driver.Hello()).

JavaScript palaiko agregaciją.
Programa išspausdina pranešimus:

I am, Suzuki weels driven by Mr. Max.
Hi, I`m your driver, Mr. Max! Let`s take a ride!

Įvykdę programą, ir perskaitę pranešimus, matome, kad JavaScript palaiko agregaciją:

  • lenktynininkas yra automobilyje (car.driver)
  • mes galime su juo „kalbėtis“ (car.driver.setName) ir išklausyti jo prisistatymą (car.driver.Hello)

Ar JavaScript palaiko paveldėjimą.

Dabar įsitikinsime, kad JavaScript palaiko paveldėjimą. Pirma sukursime klasę Human (asmuo), kuri detaliai apibūdins automobilį vairuojantį lenktynininką kaip asmenį. Kad viskas būtų paprasčiau, mūsų asmuo turės tik vieną požymį – amžių (age) ir du sąsajos metodus jam grąžinti ir įvesti.

function Human() { }
Human.prototype = {
  age: null,
  getAge: function() { return this.age },
  setAge: function(age) { this.age = age }
};

Svarbu suprasti, kad kiekvienas JavaScript objektas turi tik vieną lauką prototype. Todėl jį naudojant paveldėjimui: Lenktynininkas yra žmogus (Racer.prototype = Human.prototype), lenktynininko klasės laukas prototype yra perrašomas žmogaus prototype lauku. Tokiu būdu visos žmogaus savybės (laukai) ir veiksmai (metodai) tampa galimi ir lenktynininkui.

function Racer() { }
Racer.prototype = Human.prototype;
Racer.prototype.name = null;
Racer.prototype.getName = function() { return this.name };
Racer.prototype.setName = function(name) { this.name = name };
Racer.prototype.Hello = function() { 
  alert("Hi, I`m your driver, " + this.name + "(" + this.age + ")! Let`s take a ride!") 
};

Atkreipkite dėmesį į tai, kad pasikeitė klasės Racer laukų ir metodų aprašymo formuluotė. Dabar kiekvienas jų (name, getName, setName, Hello) aprašomas atskirai ir priskiriamas konkrečiam Racer.prototype laukui. Priskiriant kartu (žr. Racer formuluotę prieš tai) būtų perrašomas Racer.prototype laukas ir prarandamas ryšys su Human klase. Metodą Hello, papildome taip,kad į lenktynininko prisistatymą būtų įtrauktas ir jo amžius (age).

Panaudokime pakeistą klases ir pažiūrėkime kokius pranešimus rodys programa šį kartą. Kodą papildome eilute, kviečiančia žmogaus (Human) metodą setAge, kuris lenktynininkui (Racer) nustato amžių (age).

var me = new Racer();
me.setAge(24);
me.setName("Max");
var car = new Car("Suzuki");
car.setDriver(me);
car.driver.setName("Mr. Max");
car.Hello();
car.driver.Hello();

JavaScript palaiko paveldimumą.

I am, Suzuki weels driven by Mr. Max.
Hi, I`m your driver, Mr. Max(24)! Let`s take a ride!

Įvykdę programą, ekrane matome du pranešimus, kurie įrodo, kad JavaScript palaiko paveldimumą:

  • lenktynininkas turi amžių ir gali jį keisti
  • lenktynininkas (Racer) paveldi visas žmogaus (Human) savybes ir veiksmus.

Išvados.

Išsiaiškinome,kad JavaScript programavimo kalba įgyvendina šias svarbias objektinio programavimo galimybes:

  • agregaciją (aggregation) – objekto panaudojimą naujiems objektams konstruoti ir
  • paveldėjimą (inheritance) – objekto narių perkėlimą iš objekto kylantiems naujiems objektams

Kitos naudingos JavaScript OOP galimybės.

Galima parodyti, kad JavaScript sintaksė taip pat leidžia realizuoti:

  • laukų ir metodų matomumo atributų (private, public, privileged) funkcionalumą
  • metodų perkrovimą (method overriding)
  • interfeisų (interface) funkcionalumą
  • daugialypį paveldėjimą (multiple inheritance)

Visas sprendimo išeities kodas.

Nukopijuokite šį kodą į naują failą ir suteikite jam pavadinimą su plėtiniu HTML (pvz. example.html).
Atverkite šį failą bet kuria interneto naršykle ir patikrinkite rezultatus.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>JavaScript OOP</title>
</head>
<body>
  <script type="text/javascript">
    function Human() { }
    Human.prototype = {
      age: null,
      getAge: function() { return this.age },
      setAge: function(age) { this.age = age }
    };

    function Racer() { }
    Racer.prototype = Human.prototype;
    Racer.prototype.name = null;
    Racer.prototype.getName = function() { return this.name };
    Racer.prototype.setName = function(name) { this.name = name };
    Racer.prototype.Hello = function() { 
      alert("Hi, I`m your driver, " + this.name + "(" + this.age + ")! Let`s take a ride!") 
    };

    function Car(name) {
      this.name = name;
    }
    Car.prototype = {
      driver: null,
      setDriver: function() { return this.driver },
      setDriver: function(driver) { this.driver = driver },
      Hello: function() { 
        alert("I am, " + this.name + " weels driven by " + this.driver.name + "."); 
      }
    };

    var me = new Racer();
    me.setAge(24);
    me.setName("Max");
    var car = new Car("Suzuki");
    car.setDriver(me);
    car.driver.setName("Mr. Max");
    car.Hello();
    car.driver.Hello();    
  </script>
</body>
</html>

Sėkmės programuojant su JavaScript!