Uvod
Klase u JavaScript-u su samo drugačiji način predstavljanja konstruktorske funkcije i metoda iz “prototype objekata”, ali bez suštinske promene samog mehanizama nasledjivanja. Pozadina nasledjivanje klasa je poznati mehanizam “delegiranje metoda” kroz “prototype lanac nasledjivanja”.
U JavaScript-u rezervisana reč “class” samo ukazuje na to da je upitanju specijalna vrsta funkcije, koja za razliku od obične funkcije ne može da se pozove (invoke), nego se jedino može koristiti uz rezervisanu reč “new”.
Ova funkcionalnost je ubačena u jezik sa ES2015 standardom, sa namerom da poboljša čitljivost i olakša pisanje objektno orjentisanih programa. Pri definisanju izgleda sinktakse dizajneri standarda su se vodili idejom da naprave takvu sinktaksu koja će ličiti na sinktaksu većine “klasnih” jezika”, i tako pomoći programerima koji već programiraju u nekom “klasnom” jeziku” da lakše usvoje JavaScript sinktaksu.
Ako u narednim primerima uporedimo delove koda pisane različitim sintaksama, možemo uočiti da je bolja čitljivost klasne sintakse u odnosu na ES5 sintaksu:
Primer: ES5 sintaksa
1 2 3 4 5 6 7 8 9 |
var Osoba = (function () { function Osoba(ime) { this.ime = ime; } Osoba.prototype.objavi = function () { console.log("Ovo je " + this.ime); }; return Osoba; })(); |
Primer: Klasna sintaksa
Izgled klase je veoma sličan sintaksi kreiranja objekta prema ES5 standardu, mada je čitljiviji zbog manje redova i izbačenih nepotrebnih delova:
1 2 3 4 5 6 7 8 |
class Osoba{ constructor(ime) { this.ime = ime; } objavi() { console.log("Ovo je " + this.ime); } } |
NAPOMENA:
Za razliku od drugih “klasnih jezika”, kreiranje objekata u Javascript-u (čak i kada se koriste klase) nije statičko tj. nije nepromenjivo nakon deklarisanja objekta. U JavaScript-u objekat nakon instanciranja ostaje “vezan” za klasu, svojim “prototype objektom” preko lanca nasledjivanja. Stoga, ukoliko naknadno dodamo ili promenimo neku metodu u samoj klasi, tu promenu će osetiti svaki objekat instanciran na osnovu te klase.
Osnovne karakteristike
Kreiranja klasa
Pošto su klase ipak samo funkcije, kao i kod svih funkcija postoje dva načina za njihovo kreiranje:
- Function declarations
Ovo je najčešći način pisanja klasa:
123class nazivKlase {// TELO KLASE}Osnovna razlika izmedju deklaracije funkcije i deklaracije klase je ta što se klase ne “hoist-uju” pri kompajliranju na početak programskog koda kao funkcije. Tako da treba obratiti pažnju da se klasa definiše pre nego što se korisiti “new” za kreiranje novog objekta.
- Function expressions
Ovaj pristup se redje koristi, ali potpuno legalan način za kreiranje klasa:
123var nazivKlase = class {// TELO KLASE}Kao i kod obične “function expressions” ni ovde nema hoist-ovanja.
NAPOMENA:
Telo klase se izvršava u “strict mode”.
Metode
Obične metode
Metode definišemo kao funkcije, ali bez ključne reči function. Mada treba napomenuti da metode unutar klase imaju “enumerable” svojstvo setovano na false, što znači da u startu nisu dostupna “for..in” petlji.
Constructor metoda
Constructor je specijalna metoda koja kreira i inicijalizuje objekat. Ova metoda je pandan konstruktorskoj funkciji iz ES5, u njoj se definiše sve što je potrebno da se inicijalizuje novi objekat, uz napomenu da može postojati samo jedna “constructor” metoda unutar jedne klase.
1 2 3 |
constructor { // TELO METODE } |
Statičke metode
Statičke metode su metode koji ne pripadaju instanciranom objektu, već samoj klasi, stoga im se ne može pristupiti iz instanciranog objekta. Jedini pristup ovim metodama je preko same klase. Ovakve metode se definišu korišćenjem rezervisane reči “static” ispred naziva metode.
1 2 3 |
static nazivMetode { // TELO METODE } |
Get i Set metode
U mnogim programskim jezicima se pristup privatnim svojstvima ostvaruje uz korišćenje specijalnih metoda tzv. “get-era” i “set-era”. Tako je i kod JavaScript-a, kod koga se ovakve metode u okviru klase označavaju dodavanjem rezervisane reči “get/set” ispred samog naziva metode.
Primer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
class Heroj { constructor() { this._health = 100; } get objaviStanje() { console.log("Health je " + this._health); } set primljenUdarac (udarac){ if (this._health - udarac > 0){ this._health -= udarac; this.objaviStanje; } else { console.log("Heroj je mrtav!");// } } set ozdravljenje (pomoc){ if (this._health + pomoc >100){ this._health = 100; this.objaviStanje; } else { this._health += pomoc; this.objaviStanje; } } } var batman = new Heroj(); batman.objaviStanje // Vraća: "Health je 100" batman.primljenUdarac=50; // Vraca: "Health je 50" batman.ozdravljenje=20; // Vraca: "Health je 70" batman.primljenUdarac=80; // Vraca: "Heroj je mrtav!" |
NAPOMENA:
Get/Set metode se definišu u okviru klase kao metode, ali im se pristupa na isti način kao da su svojstva objekta (properties).
a) Ovakvoj metodi se unutar neke druge metode pristupa kao da je obično svojstvo objekta:
1 |
this.objaviStanje |
b) “Get” metoda se u novo generisanom objektu takodje poziva kao svojstvo objekta:
1 |
batman.objaviStanje |
c) Čak se i “set” metodi pristupa kao dodeljivanje vrednosti običnom svojstvu objekta:
1 |
batman.ozdravljenje=20; |
Nasledjivanje klasa
Sintaksa
Za nasledjivanje klasa je potrebno da se u samoj deklaraciji klase koristi ključna reč extends i naziv objekta koji se nasledjuje. Deklaracija u narednom primeru Naslednik extends Roditelj nam “govori” da se objekat Naslednik.prototype povezao za Roditelj.prototype objekatom.
1 2 3 |
class Naslednik extends Roditelj { // TELO KLASE } |
Pri kreiranju nove klase koja nasledjuje neku drugu klasu imamo obavezu da pri definisanju konstruktorske funkcije moramo unutar nje pozvati konstruktorsku funkciju roditeljske klase. Rezervisanja reč super ukazuje na roditeljsku konstruktorsku funkciju, a za pozivanje konstruktora bazične klase koristi metoda “super()”. Ovaj poziv konstruktora bazične klase mora biti izvršen na samom početku konstruktorske funkcije! (jer on definiše ključnu reč “this”. )
Ukoliko ipak ne pozovemo metodu “super()” u okviru naše konstruktorske funkcije, JavaScript engine će izbaciti error: “Must call super constructor in derived class before accessing ‘this’ or returning from derived constructor”. Error će se javiti čak i ako ne koristimo “this” u našem konstruktoru, kao u narednom primeru:
1 2 3 4 5 6 7 8 9 10 11 12 |
class Osoba { constructor() { console.log("Ja sam osoba.") } } class Programer extends Osoba { constructor() { console.log("Ja sam Programer!") } } var dragoljub = new Programer(); // Vraća Error: "Must call super constructor in derived class before accessing 'this' or returning from derived constructor" |
Znači, ukoliko u novoj klasi imamo konstruktor metodu (čak i da je prazna) “moramo da pozovemo super() metodu”!
OBJAŠNJENJE:
Pozivanjem metode super() izvršavamo ceo kod iz bazične konstruktorske funkcije i definišemo da this ukazuje na novo-generisane objekte.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Osoba { constructor() { console.log("Ja sam osoba.") } } class Programer extends Osoba { constructor() { super(); console.log("Ja sam Programer!") } } var dragoljub = new Programer(); // Vraća: "Ja sam osoba." // Vraca: "Ja sam Programer!" |
Šta se dešava ukoliko nova klasa nema definisanu constructor() metodu?
1 2 3 4 5 6 7 8 9 10 |
class Osoba { constructor() { console.log("Ja sam osoba!") } } class Programer extends Osoba { } var dragoljub = new Programer(); // "Ja sam osoba!" |
U ovome slučaju JavaScript pravi svoju internu (skrivenu) konstruktorsku funkciju umesto nas, a koja izgleda ovako:
1 2 3 |
constructor (...args){ super(...args); } |
Pristup svojstvu bazične klase
Svojstvu iz bazične klase pristupamo standardno sa “this.svojstvo”:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Osoba { constructor(ime) { this._ime = ime; } } class Programer extends Osoba { constructor(ime) { super(ime); console.log(this._ime + " je programer!") } } var dragoljub = new Programer("Dragoljub"); // Vraca "Dragoljub je programer!" |
Pristup metodi bazične klase
Rezervisana reč super predstavlja “roditeljski” konstruktor, stoga kada nam je potreban pristup metodi iz bazične klase, pristupamo mu sa “super.nazivmetode”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class Osoba { constructor(ime) { this._ime = ime; console.log("Moje ime je definisano u osnovnoj klasi."); } objaviIme() { console.log("Ja sam " + this._ime); } } class Programer extends Osoba { constructor(ime, programskiJezik) { super(ime); this.programskiJezik = programskiJezik; } objaviJezik() { super.objaviIme(); console.log(this._ime + ' koristi ' + this.programskiJezik + '.'); } } var dragoljub = new Programer('Dragoljub', 'JavaScript'); dragoljub.objaviJezik(); // "Moje ime je definisano u osnovnoj klasi." // Vraća: "Ja sam Dragoljub" // Vraća: 'Dragoljub koristi JavaScript.' |
Fino objasnjeno!
Hvala 🙂
mala greska samo u delu GET i SET metode, ne postoji set batman.ozdravljenje=20; vec batman.oobjaviStanje=20;
cisto da ne zbuni ljude koji uce
Hvala Igore, menjao sam imena metoda pa ostade stari naziv. Ispravljeno.
Sjajan kurs, svaka cast za sve