Klase u JavaScript-u

Uvod

Klase u javascript-u

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

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:

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:

  1. Function declarations

    Ovo je najčešći način pisanja klasa:

    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.

  2. Function expressions

    Ovaj pristup se redje koristi, ali potpuno legalan način za kreiranje klasa:

    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.

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.

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

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:

b) “Get” metoda se u novo generisanom objektu takodje poziva kao svojstvo objekta:

c) Čak se i “set” metodi pristupa kao dodeljivanje vrednosti običnom svojstvu objekta:

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.

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:

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.

Šta se dešava ukoliko nova klasa nema definisanu constructor() metodu?

U ovome slučaju JavaScript pravi svoju internu (skrivenu) konstruktorsku funkciju umesto nas, a koja izgleda ovako:

Pristup svojstvu bazične klase

Svojstvu iz bazične klase pristupamo standardno sa “this.svojstvo”:

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”:


1001 način kreiranja objekata u JavaScriptu

new Object()

Jedan od najosnovnijih načina je kreiranje praznog objekta sa rezervisanom reči new na osnovu ugradjene konstruktorske funkcije Object(), a zatim pojedinačno dodavati svojstava.

Literarna notacija

Ovaj tip kreiranja objekta je najjednostavniji, i zasniva se na definisanju svojstva objekta izmedju vitičastih zagrada:

js objekti

Konstruktorska f-ja

Pri korišćenju rezervisane reči new se pravi novi objekat na osnovu konstruktorske funkcije sa kopijom svih svojstava iz konstruktorske funkcije.

Novi objekat je dobio svoju kopiju svih svojstava (nisu samo delegirana), pa ukoliko sada promenimo neko svojsto u konstruktorskoj funkciji ono neće imati uticaja na instancirani objekat:

Ukoliko se planira veliki broj instanci, ovaj metod nije baš preporučljiv jer će se pri instanciranju svakog objekta instancirati i metoda, što nepotrebno troši deo resursa. U tom slučaju je bolje koristiti šablon “konstruktorska f-ja + prototype objekat” kod koga su metode izdvojene u okviru “prototype objekta”. Kod tog šablona se u nove objekte instanciraju samo svojstva iz konstruktorske funkcije, dok se metode delegiraju kroz prototype lanac nasledjivanja (što znači da nema nepotrebnih instanci metoda).

Konstruktorska f-ja + prototype objekat

Pri korišćenju rezervisane reči new se pravi novi objekat na osnovu konstruktorske funkcije sa kopijom svih svojstava iz konstruktorske funkcije i o istom trošku se povezuje instancirani objekat sa “prototype objektom” konstruktorske funkcije čime mu se delegiraju sve metode. Preporuka je da se u prototype objektu smeste metode a da konstruktorska funkcija služi za iniciranje potrebnih svojstava.

Primer

Zadaci “new” rezervisane reči

Svaka funkcija može biti konstruktorska a takva postaje tek kada se pozove rezervisana reč new. Pri korišćenju rezervisane reči new, dešavaju se sledeći procesi:

  1. Kreira se novi objekt
  2. Povezuje novi objekat sa “prototype objektom” konstruktorske funkcije nakon čega može da koristi njegove delegirane metode
  3. Ako konstruktorska funkcija ne vraća nikakvu vrednost, onda new iza kulisa dodaje “return this” tj. vraća novo generisani objekat.
  4. Rezervisana reč “new” iza kulisa “preusmerava” this, da umesto globalnog objekta ukazuje na instancirane objekate.
Šta se dešava ako pozovemo konstruktorsku f-ju bez rezervisane reči “new”?

Ukoliko pozovemo (invoke) konstruktorsku funkciju bez rezervisane reči “new”, vratiti će grešku, jer ništa nije vraćeno:

Medjutim ako prethodnoj konstruktorskoj funkciji eksplicitno dodamo “return this” i ponovo je pozovemo, ona će ispravno vraćati vrednosti svojstva:

Object.create()

Sa metodom Object.create() se pravi objekat kome se pri kreiranju definiše od kog objekta mu se delegiraju metode preko “prototype lanca” (ne dolazi do kopiranja svojstava!). Syntax-a za kreiranje objekat je:

Metoda Object.create() u pozadini izvršava sledeće taskove:
1.) Kreira se novi objekt
2.) Novi objekat se povezuje sa objektom od koga je nastao i tako se ubacuje u lanac nasledjivanja, nakon čega može ima pristup njegovim delegiranim metodama
3.) Opciono može da definiše i nova svojstva objekta.

Parametra “proto”

Prvi parametar koji se prosledjuje metodi “Object.create” je “proto”. On ukazuje na objekat od koga će naš objekat nasledjivati metode. Ukoliko pak želimo da kreiramo objekat, koji neće naslediti ničije metode tada za parametar koristimo vrednost “null”.

Opcioni “propertiesObject” paramaetri

Parametar “propertiesObject” je opcioni a kroz njega se dodaju nova svojstva konstruktorskoj funkciji. Medjutim treba naglasiti da su ovako dodata svojstva po default-u imaju sve deskriptore svojstva setovane na FALSE, pa su nepromenjiva i neupotrebljiva u for…in loop-u.

Stoga ako dodajemo svojstva na ovaj način, potrebno je i predefinisati deskriptore:

Novi objekat koji se kreira sa Object.create() se najčešće se kreira na osnovu nekog “prototype objekta”, ali za “uzor” može da se izabere bilo koji “običan” objekat. Nismo u obavezi da nova svojstva objekta definišemo kroz ovaj opcioni parametar “Object.create(), možeme ih naknadno definisati sa Object.defineProperty():

Factory Functions

Basic Factory Function

Factory function je svaka funkcija koja vraća novi objekat, a nije konstruktorska funkcija ili klasa. Ovaj šablon se koristi kada se želi sakriti način na koji se objekat instancira, jer enkapsulira definiciju objekta unutar funkcije. Objekat unutar ove funkcije može biti kreiran bilo kojim dosada prikazanim načinom.

ili još jednostavnije:

Ovaj pristup nije baš najbolji jer instanciranjem svakog novog objekta se stvara i kopija funkcije, stoga dolazi do nepotrebnog trošenja resursa. Ovu metodu koristite ukoliko se pravi samo jedna instanca objekta.

Advanced Factory functions

Pošto prethodni pristup troši resurse, potrebno je iskoristiti prednosti prototype nasledjivanja i sve metode objekta smestiti u prototype objekat. Metode smeštene u prototype objekat se instanciraju samo jednom a dostupne su svim napravljenim objektima. Takodje je potrebno povezati prototype objekat sa našom factory funkcijom.

U prethodnom primeru promenjive autoPrototype i autoFactory su globalne pa je najbolje sve to “spakovati” u IFFE.

Sada je “autoPrototype” clousure i dostupna je samo iz konstruktorske funkcije.

ES6 klase

javascript_konverzija

U JavaScript-u NE POSTOJE KLASE i nasledjivanje u obliku u kakvom postoje u drugim objektno orjentisanim programskim jezicima! Rezervisana reč “class” u JavaScript-u je samo estetska zamena (eng. syntactical sugar) za konstruktorsku funkciju i “prototype objekat”, a nasledjivanje je ustvari delegiranje svojstava i metoda kroz “prototype lanac nasledjivanja”. U JavaScript-u “klasa” je specijalna vrsta funkcije, koja za razliku od obične funkcije ne može da se pozove (invoke) nego se koristi uz rezervisanu reč new. Telo klase se izvršava u “strict mode”.
Constructor je specijalna metoda koja kreira i inicijalizuje objekat uz pomoć ključne reči “class”. Ova metoda je pandan konstruktorskoj funkciji iz ES5. Može postojati samo jedna “constructor” metoda unutar jedne klase.

NAPOMENA:
Kreiranje objekata u Javascript-u sa rezervisanom reči “class” nije statičko (nepromenjivo nakon deklarisanja objekta) kao što je u drugim programskim jezicima, jer objekat i nakon instanciranja ostaje “vezan” za klasu delegiranjem metoda iz “prototype objekta”. Ukoliko dodamo ili promenimo neku metodu u klasi, promenjena metoda će biti delegirana i napravljenom objektu.

Class syntax

Postoje dve syntax-e da definišemo klasu u JavaScript-u:

Class declarations

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.

Class expressions

Ovo je manje korišćen način definisanja klasa: