Download as pptx, pdf, or txt
Download as pptx, pdf, or txt
You are on page 1of 149

Funkcionalno

programiranje
Školska 2021/22 godina
Zimski semestar
Blok 2
Lekcija 1: Uvod
Sadržaj predavanja
• Upoznavanje sa predmetom
• Raspored časova i nastavnici
• Cilj i sadržaj predmeta
• Plan izvođenja nastave
• Literatura i okruženje za praktičan rad
• Polaganje ispita i ocenjivanje
• Osnove jezika JavaScript
• Varijable, tipovi, operatori
• Kontrola toka programa
• Strukture podataka: objekat, niz
• Funkcije
• Kontekst izvršavanja
Raspored časova i nastavnici
• Blok: Blok 2
• Predavanja:
• ponedeljak, sreda, petak 10:00 – 12:00, prof. dr Stefana
Janićijević
• Vežbe
• Beograd
• ponedeljak, sreda, petak 12:00 – 14:00, Ass. Petar Jakić
• Konsultacije:
• Prof.dr Stefana Janićijević, utorak 17-18
• Ass. Petar Jakić
Cilj predmeta
• Da se razumeju osnove funkcionalnog stila
programiranja
• Da se razume deklarativni stil programiranja
• Da se nauči funkcionalno programiranje u
odabranom jeziku JavaScript
• Da se nauči kako praviti programe funkcionalnog stila
koristeći programski jezik JavaScript
• Da se proširi i produbi znanje jezika JavaScript:
• Da se osposobi za rutinsko programiranje u jeziku JavaScript sa
naglaskom na “out of browser JavaScript” aplikacijama
• Da se razume kako jezik JavaScript radi “ispod haube”
Sadržaj predmeta
• Programski jezik JavaScript – osnove
• Funkcionalno programiranje – koncepti, principi, i
teorijske osnove,
• Funkcionalno programiranje u jeziku JavaScript
Okruženje za praktičan rad
1. Chrome DevTools skup alata za izvršavanje
JavaScript koda u brauzeru
2. Node.js okruženje za izvršavanje JavaScript koda
van brauzera.
3. Babel JavaScript transkompajler za konverziju
 ECMAScript 2015+ (ES6+) koda u ranije verzije
specifikacija (ako bude trebalo)
4. Mocha okruženje za testiranje
Literatura za predmet
1. Za JavaScript generalno
1. I. Kantor, PART 1 The JavaScript language, dostupno na: https://javascript.info/
2. Z. Konjović, Funkcionalno programiranje, slajdovi sa predavanja, dostupni na stranici predmeta
3. E. Elliot, Composing Software - An Exploration of Functional Programming and Object
Composition in JavaScript, Leanpub, 2019, Poglavlje “A Functional Programmer’s Introduction
to JavaScript”
2. Za JavaScript funkcionalno programiranje
1. K. Simpson, Functional-Light JavaScript - Balanced, Pragmatic FP in JavaScript, Manning, 2019
(prevod na srpski Z.Konjović, dostupno na stranici predmeta)
2. A. Aravinth, Beginning Functional JavaScript - Functional Programming with JavaScript Using
EcmaScript 6, Appress, 2017 (prevod na srpski, Z. Konjović, dostupno na stranici predmeta)
3. E. Elliot, Composing Software - An Exploration of Functional Programming and Object
Composition in JavaScript, Leanpub, 2019
4. Z. Konjović, Funkcionalno programiranje, slajdovi sa predavanja, dostupno na stranici predmeta
3. Priručnici za okruženja i slično
1. K. Basques, Get Started with Debugging JavaScript in Chrome DevTools, dostupno na
https://developers.google.com/web/tools/chrome-devtools/javascript (prevod na srpski Z.
Konjović, dostupan na stranici predmeta)
2. I. Kantor, Automated testing with Mocha, dostupno na: https://javascript.info/testing-mocha,
(prevod na srpski Z. Konjović, dostupan na stranici predmeta)
Polaganje ispita i ocenjivanje
• Ispit se sastoji iz 2 teorijska kolokvijuma (posle petog i desetog
termina) i 1 praktičnog zadatka (tokom ispitnog dela)
Delovi se boduju na sledeći način:
Kolokvijum 1 – 30
Kolokvijum 2 – 30
Praktični zadatak – 40

• U svakom novom ispitnom roku (ko nije završio obaveze) radi se


integralni ispit koji sadrži kompletan teorijski deo i praktični zadatak
(60+40)

• Važenje izvršenih obaveznih elemenata je 1 školska godina.


Osnove jezika JavaScript (JS)
Sadržaj
• Struktura koda JS programa
• Varijable, tipovi, operatori
• Kontrola toka programa: uslovi i grananja, petlje
• Strukture podataka: objekat, niz
• Funkcije, razrešavanje identifikatora, dosezanje,
zatvaranje
Struktura koda JS programa
• Kod JS programa sastoji se iz naredbi i komentara
• Naredbe su sintaksni konstrukti i komande koji
dovode do izvršavanja akcija.
• Može ih biti više (međusobno se razdvajaju simbolom ;koji
se može i izostaviti)
• Postoje različite vrste: deklaracije, izrazi, dodele, uslovne
naredbe, petlje i skokovi
• Komentari ne proizvode nikakvo izvršavanje, niti utiču
na izvršavanje naredbi
• Jednolinijski //
• Višelinijski /* tekst komentara */
Izrazi, vrednosti, varijable
• Izraz je svaki deo koda koji se evaluira na vrednost
• Varijabla (promenljiva) je imenovano skladište za
podatke/vrednosti i ona se mora kreirati.
• Varijable se u JS programu kreiraju deklaracijama let,
const i var ( deklaracija var je iz ranijih verzija
specifikacije jezika)
• Deklaracija const deklariše konstantu – vrednost koja ne
može biti promenjena.
• Osnovna razlika između let i var je u pravilima dosezanja:
• let i const: doseg je blok ({}) u kome je deklaracija
• var : doseg je funkcija u kojoj je deklaracija
Striktna i nestriktna (lenja) evaluacija
• Koncept se odnosi na način obrade argumenata pri evaluaciji izraza.
• Pri striktnoj evaluaciji vrši se obrada argumenata "unapred", baz obzira da li je
to potrebno u trenutnoj fazi izvršavanja.
• Pri lenjoj evaluaciji obrada se vrši u trenutku kada je to potrebno za izvršavanje
programa.
• Tehnička razlika je u denotacionoj semantici izraza koji sadrže
računanja koja mogu da otkažu.
• Pri striktnoj evaluaciji, evaluacija svakog termina koji sadrži podtermina koji
otkazuje takođe otkazuje. Na primer, izraz:
print length([2+1, 3*2, 1/0, 5-4])
otkazuje u slučaju striktne evaluacije zbog deljenja nulom u trećem članu liste,
a ne otkazuje pri lenjoj evaluaciji jer za određivanje dužine liste nije potrebno
sračunavati vrednosti članova liste.
• Većina jezika primenjuje striktnu evaluaciju kao pretpostavljenu, ali
pruža i mogućnost lenje evaluacije.
Deklarisanje varijable: primer
// deklarisanje jedne varijable
var cost;
// deklarisanje više varijabli razdvojenih zarezima
var cost, profit;
// Deklarisanje jedne varijable i dodela vrednosti
var cost = 120;
// Deklarisanje više varijabli i dodela vrednosti
var cost = 120, profit = 77;
JS tipiziranost i tipovi
• Svi programski jezici imaju ugrađene tipove i
strukture podataka.
• Oni se mogu koristiti za pravljenje novih struktura.
• JavaScript je  slabo tipiziran i dinamički jezik.
JS slaba tipiziranost
• JavaScript je slabo tipiziran (dinamički ) jezik, što znači da varijabla
u JavaScript-u može da skladišti bilo koju vrstu podataka, odnosno
različite vrste podataka u različitim trenucima
• Ispravno je:
let message = "hello";
message = 123.456;
• JavaScript ima tipove podataka
• Tip podatka može se dobaviti operatorom typeof:
let message = "hello";
typeof (message); // "string"
• Primitivni tipovi su nepromenljivi (imutabilni): nije moguće
promenuti interno stanje – svaka promena kreira novu instancu
promenljive. Kod objekata to nije slučaj.
Primer slabe tipiziranosti
function test1 () {
let message = {};
alert (message)}

message = 123.456;
alert (message); // Ispis: 123.456
message = 'Sada sam string, a bila sam broj'
alert (message); // Ispis: Sada sam string, a
// bila sam broj
test1(); // Ispis:[object Object]
JS tipovi i strukture podataka
• Poslednja verzija ECMAScript standarda definiše
devet tipova klasifikovanih u 3 grupe
1. Primitivni tipovi
2. Strukturalni tipovi
3. Strukturalna korenska primitiva
JS primitivni tipovi
• U JavaScript-u, primitiva (primitivna vrednost, primitivni tip
podatka) je podatak koji nije objekat i nema metode. Ima ih 6:
• undefined : typeof instance === "undefined"
• Boolean : typeof instance === "boolean"
• Number : typeof instance === "number"
• String : typeof instance === "string"
• BigInt : typeof instance === "bigint"
• Symbol : typeof instance === "symbol„
• Sve primitive su imutabilne, t.j., ne mogu se menjati
• Ne sme se brkati sama primitiva sa varijablom kojoj je dodeljena
primitivna vrednost. Varijblama se može ponovo dodeljivati nova
vrednost, ali se postojeća vrednost ne može menjati na način na koji
se mogu menati objekti, nizovi i funkcije.
JS strukturalni tipovi
• Object : typeof instance === "object".
• Specijalni strukturalni (non-data) tip za svaku
konstruisanu instancu objekta koji se koristi i kao
strukture podataka: new Object, new Array, new Map,
new Set, new WeakMap, new WeakSet, new Date i
skoro sve ostalo što se pravi pomoću ključne reči new.
• Function : typeof instance === "function".
• Takođe non-data struktura, iako reaguje na typeof
operator: to je samo specijalna skraćenica za funkcije,
iako je svaki konstruktor funkcije izveden iza
konstruktora objekta.
JS strukturalna korenska
primitiva
• null : typeof instance === "object".
• Specijalni primitivni tip čija se vrednost dodatno koristi:
on je koren u lancu nasleđivanja objekata – svaki objekat
nasleđuje se od null tipa;
Konverzija tipova
• Različiti operatori i funkcije zahtevaju tačno određene tipove podataka
• Na primer, aritmetičke operacije zahtevaju podatke tipa number, operacije
ispisa zahtevaju podatke tipa string.
• Zbog toga je potrebno da se izvrši konverzija tipova, na primer:
• 1+2 = 3 => “3”
• “3”, “4” => 3+4
• Postoje situacije kada se mora izvršiti eksplicitna konverzija tipova, ali
većina operacija radi automatsku (implicitnu) konverziju tipova po
zadatim pravilima koja treba znati da ne bi bilo “iznenađenja”
• Posebno, treba proučiti pravila za konverziju tipa object u primitivne tipove
• Dobar izvor informacija o konverzijama je
https://javascript.info/type-conversions (za primitivne tipove) i
https://javascript.info/object-toprimitive (za konverziju tipa object u
primitivne tipove).
Operatori i operandi
• Operand je ono na šta se primenjuje operator
• 2+3
• Operatori:
• Unarni (primenjuje se na 1 operand):
• Znak - je negacija: let x = 1; x = -x;
• Znak + je konverzija u numeric: alert( +true ); // 1; alert( + "" ); // 0
• Binarni (primenjuje se na 2 operanda): let x = 1, y = 3; alert( y - x );
• Znak - je oduzimanje: let x = 1, y = 3; y = 3; alert( y - x ); // 2
• Znak + je sabiranje: let x = 1, y = 3; y = 3; alert( y + x ); // 4
• Znak + je konkatenacija stringova: let s = "my" + "string"; alert(s); // mystring
• Znak = je dodela vrednosti: let x = 1, y = 3;
• Za operatore je definisan redosled primene: 1 + 2 * 2 => 1 + (2*2)
• Sve o operatorima imate na https://javascript.info/operators
Operatori poređenja
• Operatori
• Veće/manje od: a > b, a < b.
• Veće/manje ili jednako od: a >= b, a <= b.
• Jednako: a == b.
• Nije jednako: a != b.
• Rezultat poređenja je uvek bulovski tip
• Stringovi se porede “slovo”-po-”slovo” leksikografskim
redosledom:
• ‘Ena' < ‘Ana'
• ‘Dobar' > ‘Dobra‘
• Detaljno o operatorima poređenja na
https://javascript.info/comparison
Logički operatori
• || (OR), && (AND), ! (NOT)
a b c = a ||b c = a &&b c = !a/c = !b Napomena
true true true true false/false

Prioritet
true false true false false/true operatora &&
je viši od
prioriteta
false true true false true/false operatora ||
• Više
false false false false true/true

• Detalji o logičkim operatorima na https://javascript.info/logical-operators


Kontrola toka programa
• Provera uslova: If naredba
• Uslovna dodela vrednosti ?
• switch naredba
• Petlje
• while,
• do ... while
• for petlja
Operatori provere uslova: If naredba
• If naredba:
If (uslov){
//kod koji se izvršava ako uslov ima vrednost true
} else {
//kod koji se izvršava ako uslov ima vrednost
false}
// kod koji se izvršava nakon izvršenja If bloka.
• If (uslov) naredba sračunava uslov i rezultat
automatski konvertuje u bulovsku vrednost (true
ili false).
If naredba: primeri
• Bez else:
let year = prompt('Koje godine je publikovana ECMAScript-2015 specifikacija?', '');
if (year == 2015) {

alert("Ispravno!" );

alert("Baš ste pametni!" );


}
• Sa (više) else:
let year = prompt('Koje godine je publikovana ECMAScript-2015 specifikacija?', '');

if (year < 2015) {


alert('Prerano...' );
} else if (year > 2015) {
alert('Prekasno ...' );
} else {
alert('Ispravno!' );
}
Uslovna dodela vrednosti - operator “?”
• Uslovna (može i sa If, ali je kraće sa “?”) dodela vrednosti:
let rezultat = uslov ? vrednost1 : vrednost2;

• Ako je uslov true, rezultat = vrednost1; ako je uslov


false, rezultat = vrednost2
• Primer:
let uzrast = prompt("Vaše godine, molim?");

let message = (uzrast < 3) ? 'Ćao, bebice!' :


(uzrast < 18) ? 'Zdravo, drugar!' :
(uzrast < 60) ? 'Poštovanje!' :
'Vaše godine su zaista za poštovanje!';

alert( message );
Naredba switch:sintaksa

switch(x) {
case 'value1': // ako je (x === 'value1')
...
[break]

case 'value2': // ako je (x === 'value2')


...
[break]

default:
...
[break]
}
Naredba switch:opcija break

let a = 2 + 2;
let a = 2 + 2;
switch (a) {
case 3: switch (a) {
alert('Premalo' );
case 3:
break;
case 4: alert('Premalo' );
alert(' Tačno!' ); case 4:
break; alert('Tačno!' );
case 5: case 5:
alert(' Preveliko' );
alert('Preveliko' );
break;
default: default:
alert(' Ne znam ' ); alert("Ne znam" );
} }
Petlje: while i do ... while
• while petlja: prvo se proverava uslov, pa se izvršava
telo
let i = 0;
while (i < 3) { // ispiši 0, pa 1, pa 2
alert( i );
i++;
}

• do ... while petlja: prvo se izvršava telo, pa se


proverava uslov
let i = 0;
do {
alert( i );
i++;
} while (i < 3);
Petlje: for petlja
• Petlja sa “brojačem” – varijablom koja kontroliše izvršavanje tela petlje
for (begin; condition; step) {
// ... telo petlje ...
}
begin – početna vrednost brojača
condition – uslov završavanja petlje (terminalna vrednost brojača)
step – korak sa kojim se povećava vrednost brojača, najčešće 1
• Jednostavan primer (sa korakom 1):
for (let i = 0; i < 3; i++) { // ispisuje 0, zatim 1, zatim 2
alert(i);
}
• Jednostavan primer (sa korakom različitim od 1):
for (i=5; i <= 20; i += 5){
alert (i);// ispisuje 5, zatim 10, zatim 15, zatim 20

}
Vidljivost brojačke varijable
• Ako je brojačka varijabla deklarisana u petlji, ona je vidljiva
samo u petlji:
for (let i = 0; i < 3; i++) {
alert(i); // ispisaće : 0, 1, 2
}
alert(i); // Ispisaće poruku o grešci:Uncaught ReferenceError: // i
is not defined at <anonymous>:4:7

• Ako je brojačka varijabla deklarisan ranije ona je vidljiva i van


petlje:
let i = 0;

for (i = 0; i < 3; i++) { // koristi se već deklarisana varijabla


alert(i); // ispisuje 0, zatim 1, zatim 2
}

alert(i); // ispisaće 3, jer je i vidljivo


Sažeta sintaksa
• Svaki deo for naredbe koji se nalazi u maloj zagradi
može se izostaviti
• Izostavljen begin
let i = 0; // izvršena deklaracija i dodela vrednosti
for (; i < 3; i++) { // ne treba nam "begin"
alert( i ); // 0, 1, 2
}

• Izostavljen begin i step


let i = 0;
for (; i < 3;) {
alert( i++ );
}

• Izostavljeno sve – beskonačna petlja


for (;;) {
// ponavlja se bez ograničenja
}
for petlja: “Iskakanje” iz petlje
• Normalno se iz petlje izlazi kada je terminalni uslov
zadovoljen.
• Može se prisilno prekinuti petlja direktivom break:
let sum = 0;
while (true) {
let value = +prompt("Unesite broj", '');
if (!value) break; // (*)
sum += value;
}
alert( 'Sum: ' + sum );
Direktiva continue: zaustavljanje tekuće
iteracije
• Ne prekida celu petlju, zaustavlja tekuću iteraciju i prisilno počinje sledeću (ako uslov
dozvoljava):
for (let i = 0; i < 10; i++) {

// ako je true, preskoči ispis


if (i % 2 == 0) continue;

alert(i); // 1, then 3, 5, 7, 9
}
• Može isto i bez continue:
for (let i = 0; i < 10; i++) {

if (i % 2) {
alert( i );
}

}
• A ovo ne može (continue nije dozvoljeno desno od ?):
(i > 5) ? alert(i) : continue; // continue ovde nije dozvoljeno
Labeliranje za
break/continue
• Koristi se za kada je potrebno istovremeno “iskočiti”
iz više petlji:
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Vrednost u tački (${i},${j})`, '');
// Šta ako želimo da iskočimo na Done (iz petlje i i petlje j?)
}
}
alert('Done!');

• Za to se koristi labela kojom se označava petlja


(label)
Labeliranje za break/continue: primer
Labela
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Vrednost u tački (${i},${j})`, '');
// ako se unese prazan string ili kontrolni karakter,iskače se iz obe petlje
if (!input) break outer; // (*)
// uradi nešto sa vrednošću...
}
}
alert('Gotovo!');

•Direktiva break/continue se može pozvati samo iz unutrašnjosti petlje a


labela mora da bude negde iznad direktive. Znači, ne može:
break label; // ne skače na label ispod
label: for (...)
Strukture podataka: objekat i
niz
Struktura podataka: object
• Tip object (plain object) služi za skladištenje kolekcija
(neuređenih) različitih vrednosti gde se pojedinačnim elementima
kolekcije može pristupati (putem ključa), i skladištenje drugih složenijih
entiteta.
• Objekat ima ime i svojstva
• Svojstvo ima ključ/ime (tip String ili Symbol) i vrednost (bilo koji tip)
let korisnik = { // objekat sa imenom korisnik
ime: "Petar", // ključ "ime", vrednost „Petar"
starost: 30 // ključ "starost" ,vrednost 30
};

• U objektima, imena svojstava su uvek stringovi. Ako se koristi bilo koji


drugi tip kao ime svojstva, on će uvek prvo biti konvertovan u string.
• Postoje i specijalizovane vrste objekata: nizovi (Array), datumi (Date),
greške (Error), ....
Objekat: Reprezentacija u
memoriji
• ECMAScript specifikacija u suštini objekte definiše
kao rečnike sa ključevima koji pokazuju na atribute
svojstva.
Operacije sa objektima
• Kreiranje objekta: dve notacije
• Pristup svojstvu: dve notacije, geteri i seteri
• Brisanje/dodavanja svojstva
• Provera postojanja svojstva
• Kopiranje objekta: plitko i duboko
• Iteriranje nad svojstvima u objektu
Operacije sa objektima: kreiranje objekta
• Kreiranje (“praznog”) objekta – dve sintakse:
let korisnik = {}; // "literalna" sintaksa koja se najčešće koristi
korisnik = new Object(); // „konstruktorska" sintaksa

• Kreiranje “nepraznog” objekta - literalno


let korisnik = { // objekat sa imenom korisnik
ime: "Petar", // ključ "ime" sa vrednošću „Petar"
starost: 30 // ključ "starost" sa vrednošću 30
};

• Kreiranje “nepraznog” objekta – konstruktorski


let korisnik = new Object(); // objekat sa imenom korisnik

korisnik.ime; // svojstvo "ime" bez dodeljene vrednosti


korisnik.starost; // svojstvo "starost" bez dodeljene vrednosti
console.log( korisnik);
console.log (korisnik.ime);
console.log (korisnik.starost);

korisnik.ime = "neko"; // svojstvu "ime" dodeljuje se vrednost "neko"


korisnik.starost = 33; // svojstvu "starost" dodeljuje se vrednost 33
console.log( korisnik);
console.log (korisnik.ime);
console.log (korisnik.starost);
Objekat: sadržaj1
• Sadržaj objekta sastoji se od vrednosti (bilo kog tipa) skladištenih
(zapisanih) u svojstvima.
• U objektu se skladište samo ključevi/imena svojstava, a sami
sadržaji se skladište negde “izvan”.
• Pristup sadržaju:
var myObject = {
a:2
};
 
myObject.a; // 2 – dozvoljava samo sintaksno
// ispravne konstrukte za ime
myObject["a"]; // 2 – dozvoljava proizvoljan
// UTF-8/Unocode string za ime
Objekat: sadržaj2
• Sintaksa [".."] koristi vrednost stringa da specificira
lokaciju, što znači da sam program može da
generiše vrednost tog stringa:
var wantA = true;
var myObject = {
a:2
};
 var idx;
 if (wantA) {
idx ="a";
}
// kasnije
console.log( myObject[idx] ); // 2
Objekat: sadržaj3
• A ime se može i "sračunati":
var prefix = "foo";
 
var myObject = {
[prefix +"bar"]:"hello",
[prefix +"baz"]:"world"
};
 
myObject["foobar"]; // hello
myObject["foobaz"]; // world
Sadržaj objekta: geteri
• Vrlo često je potrebno pribavljanje različitih sadržaja objekta:
// Poslednji
const obj = {
log: ['a', 'b', 'c'],
get poslednji() {
if (this.log.length === 0) {
return undefined;
}
return this.log[this.log.length - 1];
}
};
console.log(obj.poslednji); // Izlaz: "c„

// Prvi
const obj = {
log: ['a', 'b', 'c'],
get prvi() {
if (this.log.length === 0) {
return undefined;
}
return this.log[this.log.length - 3];
}
};
console.log(obj.prvi); // Izlaz: „a"
JS get funkcija
• Ponekad je poželjno da se dozvoli pristup svojstvu koje vraća dinamički
sračunatu vrednost, ili da se reflektuje status interne varijable bez
eksplicitnog poziva metode. U JS-u se to postiže korišćenjem getera
(getters).
• Sintaksa
{get prop() { ... } }
{get [expression]() { ... } }
• prop – ime svojstva koje će se vezati za zadatu funkciju.
• expression – Od ECMAScript 2015, mogu se koristiti i izrazi za sračunato ime
svojstva za vezivanje za zadatu funkciju.
• Nije dozvoljeno svojstvo koje istovremeno efektivno sadrži vrednost i
ima za sebe vezan geter.
• Geteri daju mogućnost da se svojstvo definiše ali njegovu vrednost ne
sračunavaju dok se svojstvu ne pristupi.
• Ako se svojstvu nikada ne pristupi, njegova vrednost se ni ne sračunava.
Definisanje getera: novi objekat
• Za nove objekte, u inicijalizatorima objekata
const obj = {
log: ['example','test'],
get latest() {
if (this.log.length === 0) return
undefined;
return this.log[this.log.length - 1];
}
}
console.log(obj.latest); // "test"
Definisanje getera: postojeći objekat
• Za postojeće objekte, pomoću metode
Object.defineProperty():
const o = {a: 0};

Object.defineProperty(o, 'b', { get:


function() { return this.a + 1; } });

console.log(o.b) // pokreće geter koji


//vraća a + 1 (što je 1)
Geter: sračunato ime svojstva
const expr = 'foo';

const obj = {
get [expr]() { return 'bar'; }
};

console.log(obj.foo); // "bar"
Sadržaj objekta: seteri
• Mehanizam setera povezuje svojstvo objekta sa funkcijom koja će biti
pozvana svaki put kada se pokuša postavljanje tog svojstva.
• JS sintaksa
{set prop(val) { . . . }}
{set [expression](val) { . . . }}
Parametri:
prop – ime svojstva za koje se vezuje data funkcija.
val - alias za varijablu koja sadrži vrednost koja se dodeljuje svojstvu prop.
expression – Od ECMAScript 2015, moguće je korišćenje izraza za
sračunavanje imena svojstva za koje će se vezati data funkcija.
• Kao i kod getera, nije dozvoljeno imati seter na svojstvu koje sadrži
aktuelnu vrednost.
• Seteri se najčešće koriste zajedno sa geterima za kreiranje neke vrste
pseoudo-svojstva.
Definisanje setera: nov objekat
const language = {
set current(name) {
this.log.push(name);
},
log: []
}

language.current = 'EN';
console.log(language.log); // ['EN']

language.current = 'FA';
console.log(language.log); // ['EN', 'FA']
Definisanje setera: postojeći objekat
• Koristi se defineProperty():
const o = {a: 0};

Object.defineProperty(o, 'b', {
set: function(x) { this.a = x / 2; }
});

o.b = 10;
// Pokreće seter koji dodeljuje 10/2 (5)
// svojstvu 'a'

console.log(o.a) // 5
Seter: sračunato ime svojstva
const expr = 'foo';

const obj = {
baz: 'bar',
set [expr](v) { this.baz = v; }
};

console.log(obj.baz);
// "bar"

obj.foo = 'baz';
// pokretanje setera

console.log(obj.baz);
// "baz"
Brisanje getera i setera
• Koristi se operator delete:
delete language.current; // seter iz primera
delete obj.latest; // geter iz primera
Geteri i seteri: primer
var o = {
a: 7, // ovo je obična vrednost
get b() { // ovo je geter
return this.a + 1;
},
set c(x) { // ovo je seter
this.a = x / 2;
}
};
 
console.log(o.a); // 7
// sledeća linija pokreće get b() metodu
console.log(o.b); // 8
// Sledeća linija pokreće funkciju set c(x)
o.c = 50;
console.log(o.a); // 25
Operacije sa objektima: dodavanje i
brisanje svojstva
• Dve sintakse
// “tačkasta” sintaksa (postavlja svojstvo i njegovu vrednost)
let user = { // objekat
name: "John", // svojstvo "name" vrednost "John"
age: 30 // svojstvo "age" vrednost 30
};
alert( user.name);
alert( user.age);
// Brisanje svojstava (operator delete):
delete user.name
alert( user.name ); // undefined
alert( user.age ); // 30

// sintaksa “srednje zagrade” (ako se ime svojstva sastoji od više reči)


let user1 = {};
alert( user1);
// postavi svojstvo i njegovu vrednost
user1["likes birds"] = true;
// pribavi vrednost svojstva
alert(user1["likes birds"]); // true
// naredba delete briše svojstvo
delete user1["likes birds"];
alert(user1["likes birds"]); // undefined
Provera postojanja svojstva1
• JS omogućuje da se proveri postojanje svojstva u
objektu:
1. hasOwnProperty() metod
2. in operator
3. Poređenje sa undefined
Provera postojanja svojstva2
// 1. način
const hero = {
name: 'Batman'
};

hero.hasOwnProperty('name'); // => true


hero.hasOwnProperty('realName'); // => false
// 2. način
const hero = {
name: 'Batman'
};

'name' in hero; // => true


'realName' in hero; // => false
// 3. način
const hero = {
name: 'Batman'
};

hero.name; // => 'Batman'


hero.realName; // => undefined
Operacije sa objektima: kopiranje objekta
• Dve vrste kopiranja: plitko i duboko
• Plitko kopiranje: Ne pravi se potpuno nov objekat, već se kopiraju
samo svojstva primitivnog tipa, a za ugnježdene objekte se
kopiraju reference (adrese u memoriji) na postojeći objekat.
Operacije nad ugnježdenim objektom se, u stvari, izvršavaju nad
originalnim ugnježdenim objektom.
• Duboko kopiranje: “Pravo” duboko kopiranje (trebalo bi da) pravi
kompletnu kopiju svakog objekta na koji naiđe.
• Implementacija dubokog kopiranja ima raznih i nisu baš sve
potpuno dosledne definiciji.
• Jedan dobar izvor informacija o JS kopiranju objekata je
https://www.digitalocean.com/community/tutorials/copyin
g-objects-in-javascript
JS plitko kopiranje objekata
• Korišćenje metode Object.assign()
• Koristi se za kopiranje vrednosti svih prebrojivih sopstvenih svojstava
jednog ili više izvornih objekata u ciljni objekat.
let obj = {
a: 1,
b: 2,
};
let objCopy = Object.assign({}, obj);

console.log(objCopy); // rezultat - { a: 1, b: 2 }
objCopy.b = 89;
console.log(objCopy); // rezultat - { a: 1, b: 89 }
console.log(obj); // rezultat - { a: 1, b: 2 }
JS plitko kopiranje: problem
let obj = {
a: 1,
b: {
c: 2,
},
}
let newObj = Object.assign({}, obj); // obj plitko kopiran u newObj
console.log(newObj); // { a: 1, b: { c: 2} }

obj.a = 10; // a promenjeno u obj, ali nije dodeljeno newObj-u


console.log(obj); // { a: 10, b: { c: 2} }
console.log(newObj); // { a: 1, b: { c: 2} }

newObj.a = 20;// a promenjeno u newObj, ali nije dodeljeno obj-u


console.log(obj); // { a: 10, b: { c: 2} }
console.log(newObj); // { a: 20, b: { c: 2} }

newObj.b.c = 30; // c promenjeno u newObj


console.log(obj); // { a: 10, b: { c: 30} }
console.log(newObj); // { a: 20, b: { c: 30} }

// Dakle, newObj.b.c = 30 i obj.b.c = 30; Zašto?


JS duboko kopiranje1
• Korišćenje JSON.parse(JSON.stringify(object))
let obj = {
a: 1,
b: {
c: 2,
},
}

let newObj = JSON.parse(JSON.stringify(obj));

obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }
//(newObj netaknut!)
JS duboko kopiranje2
• Jedan problem:
JSON.parse(JSON.stringify(object)) ne
radi za metode objekta koje definiše korisnik
• Rešenje: to se može raditi sa Object.assign()

• https://www.digitalocean.com/community/tutorial
s/copying-objects-in-javascript
JS duboko kopiranje: Lodash
_.cloneDeep() metod1
• Lodash je jedna od JS biblioteka čija metoda  _.cloneDeep() vrši rekurzivno
kloniranje.
const _ = require('lodash');
var obj = [{ x: 1 }, {y: 2}];
// Duboka kopija
var deepCopy = _.cloneDeep(obj);
console.log(Poređenje originala sa dubokom kopijom: ',
obj[0] === deepCopy[0]);
// Izmena originalne vrednosti
obj[0].x = 10;

// Vrednosti nakon izmene originalne vrednosti


console.log("Nakon izmene originalne vrednosti");

console.log("Originalna vrednost: ", obj);

console.log("Duboka kopija vrednost: ", deepCopy);


JS duboko kopiranje: Lodash
_.cloneDeep() metod2

Rezultat:
Poređenje originala sa dubokom kopijom: false
Nakon izmene originalne vrednosti obj[0].x = 10;:
Originalna vrednost: [ { x: 10 }, { y: 2 } ]
Duboka kopija vrednost: [ { x: 1 }, { y: 2 } ]
Plitko kopiranje: još primera
let user = { name: 'John' };
alert("user.name:" + user.name); // user.name je John
let admin = user; // kopiranje po referenci (user -> admin)
alert("admin.name:" + admin.name);// admin.name je John

admin.name = 'Pete'; // izmena preko "admin" reference


alert("admin.name:" + admin.name); // admin.name je Pete

alert("user.name:" + user.name); // user.name je isto Pete


Duboko kopiranje: još primera
let user = { name: "John" };
console.log( user );

let permissions1 = { canView: true };


let permissions2 = { canEdit: true };

// kopira sva svojstva iz permissions1 i permissions2 u user


Object.assign(user, permissions1, permissions2);
console.log( user );

// sada je user = { name: "John", canView: true, canEdit: true }


Operacije sa objektima: iteriranje nad
svojstvima objekta
• Za prolazak kroz sva svojstva objekta postoji specijalni oblik petlje : for..in.
• Sintaksa
for (key in object) {
// ovo se izvršava za svako svojstvo objekta
}
• Primer:
let korisnik = {
ime: "Petar",
godine: 30,
jeAdmin: true
};
for (let key in korisnik) {
// Ispiši imena svojstava
alert( key ); // ime, godine, jeAdmin
// Ispiši vrednosti svojstava
alert( korisnik[key] ); // Petar, 30, true
}
Objekti: deskriptori svojstava
• Deskriptor svojstava (zvani i deskriptor podatka) za
normalno svojstvo objekta je mnogo više od same
vrednosti (value) svojstva.
• On obuhvata još tri krakteristike:
writable, enumerable, i configurable.
• Vrednosti koje ove tri karakteristike mogu da uzmu
su true ili false.
Deskriptor: writable
• Kontroliše mogućnost da se promeni vrednost bilo svojstva objekta (
u režimu strict dobija se poruka o grešci TypeError):

var myObject = {};

Object.defineProperty( myObject, "a", {


value:2,
writable:false, // nije writable!
configurable:true,
enumerable:true
} );

myObject.a = 3;
myObject.a; // 2
Deskriptor: configurable
• Sve dok svojstvo ima karakteristiku da je
konfigurabilno (vrednost deskriptora configurable
je true), moguće je modifikovati definiciju
deskriptora pomoću defineProperty(..).
• configurable:false znači da više nema menjanja
• Promena  configurable na false je jednosmerna
akcija i ne može se opozvati!
• Takođe, configurable:false sprečava mogućnost
da se koristi delete operator za uklanjanje
postojećeg svojstva.
Deskriptor: enumerable
• Ova karakteristika kontroliše da li će se svojstvo
pojavljivati u konstruktima sa nabrajanjima svojstva
u objektu, kao što je for..in petlja.
• Vrednost svojstva false znači da se ono neće
pojavljivati u takvim nabrajanjima, iako je inače
potpuno dostupno.
• Naravno, vrednost true znači da će se pojavljivati u
takvim nabrajanjima.
Strukture podataka: Array1
• Tip Array (niz) je specijalni tip objekta koji služi za
skladištenje uređenih kolekcija vrednosti gde se
pojedinačnim elementima kolekcije može pristupati
putem pozicije (indeks).
• Deklarisanje – dve sintakse za kreiranje praznog niza:
let arr = new Array(); - vrlo retko se koristi
let arr = []; - najčešće se koristi
• Dodela vrednosti elementima može da se radi u
deklaraciji i van deklaracije
• Početna vrednost indeksa je 0.
Strukture podataka: Array2
• Ukupan broj elemenata u nizu čuva se u svojstvu length što predstavlja
vrednost poslednjeg numeričkog indeksa uvećanu za 1 (može da bude i
“praznih” elemenata između):
// U deklaraciji
let fruits = ["Apple", "Orange", "Plum"];
alert (fruits.length)// 3
alert (fruits) // Apple Orange Plum
// Van deklaracije
fruits [3] = "Grape"  
alert (fruits.length) // 4
alert( fruits); // Apple Orange Plum Grape
fruits [9] = "Pear"  
alert (fruits.length) // 10
alert( fruits);
// Apple Orange Plum Grape ,,,,,,Pear
Deklarisanje niza: ključna reč
new
• Sintaksa je:
let arr = new Array("Apple", "Pear", "etc");
• Sintaksa se retko koristi, jer ima “škakljivo” svojstvo: Ako se new
Array pozove sa jednim argumentom koji je broj (number),
poziv kreira niz bez elemenata ali sa zadatom dužinom
(length).
let arr = new Array(2); // da li će kreirati [2] ?
alert( arr[0] ); // undefined! Nema elemenata.
alert( arr.length ); // a dužina 2
• Ako nije number, radi uredno:
let arr = new Array("2"); // da li će kreirati ["2"] ?
alert( arr[0] ); // 2! Ima element koji treba.
alert( arr.length ); // a dužina 1
Strukture podataka Array : operacije
• Kraj niza
• push(...stavke) dodaj na kraj i vrati.
• pop() ukloni sa kraja.
let fruits = ["Apple", "Orange", "Pear"];
alert( fruits ); // Apple, Orange, Pear
fruits.pop(); // ukloni "Pear"
alert( fruits ); // Apple, Orange
fruits.push ("Plum") // dodaj "Plum" na kraj
alert( fruits ); // Apple, Orange, Plum
Strukture podataka Array : operacije
• Početak niza
• shift() ukloni sa početka i vrati:
let fruits = ["Apple", "Orange", "Pear"];
alert( fruits ); // Apple, Orange, Pear
fruits.shift(); // ukloni Apple
alert( fruits ); // Orange, Pear

• unshift(...stavke) dodaj na početak:


let fruits = ["Orange", "Pear"];
alert( fruits ); // Orange, Pear
fruits.unshift('Apple');
alert( fruits ); // Apple, Orange, Pear
Array: ostale operacije
• Brisanje elementa: splice i slice
• Spajanje nizova: concat
• Iteriranje nad nizom: forEach
• Pretraživanje u nizu: 
• arr.indexOf,  arr.lastIndexOf ,  arr.includes ,  
arr.filter(fn).
• Transformisanje niza:
• arr.map(..), sort(fn), arr.reverse(..), split i join,
arr.reduce  i  arr.reduceRight
• Identifikacija tipa: Array.isArray(..)
Array : metoda splice1
• “Švajcarska brica za nizove”: umetanje, uklanjanje i
zamena elemenata.
• Sintaksa:
arr.splice(index[, deleteCount, elem1, ..., elemN])
• Semantika:
arr je ime niza na koji se primenjuje metoda
Počinjući od pozicije index uklanja deleteCount elemenata (ako je
deleteCount = 0, ne uklanja ništa) i zatim umeće elemente
elem1, ..., elemN na njihovo mesto. Vraća niz uklonjenih elemenata.
Array : metoda splice2
Primer 1 (uklanjanje i zamena elemenata niza):
let arr = [“Ja", “učim", "JavaScript", “baš", “sada"];
// uklanja tri prva elementa i zamenjuje sa druga dva
arr.splice(0, 3, “Hajde", “da igramo");
alert( arr ) // now [“Hajde", “da igramo", “baš",
“sada"]

Primer 2 (uklanjanje i prikaz niza uklonjenih):


let arr = [“Ja", “učim", "JavaScript", “baš", “sada"];
// uklanja 2 prva elementa
let removed = arr.splice(0, 2);
alert( removed ); // “Ja", “učim" <-- niz uklonjenih
// elemenata
Iteriranje nad nizom: metoda arr.forEach
• Omogućuje pokretanje funkcije nad svakim elementom
niza.
• Sintaksa:
arr.forEach(function(item, index, array) {
// ... Uradi nešto sa item
});
• Primer:
["Bilbo", "Gandalf", "Nazgul"].forEach((item,
index, array) => {
alert(`${item} ima vrednost indeksa ${index} u
${array}`);
});
Transformisanje niza: metoda arr.map
• Sintaksa
let result = arr.map(function(item, index, array) {
// vraća novu vrednost umesto item
});
• Primer (transformiše elemente na broj karaktera):
let lengths = ["Bilbo", "Gandalf",
"Nazgul"].map(item => item.length);
alert(lengths); // 5,7,6
Transformisanje niza: metoda
arr.reduce/arr.reduceRight
• Sintaksa
let value = arr.reduce(function(accumulator, item,
index, array) {
// ...
}, [initial]);
• Semantika: Funkcija se primenjuje na sve elemente
niza redom, “noseći” svoj rezultat u sledeći poziv.
• Primer (sabira elemente):
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current) => sum +
current, 0);
alert(result); // 15
arr.reduce: Izvršavanje
sum current result
Prvi poziv 0 1 1
Drugi poziv 1 2 3
Treći poziv 3 3 6
Četvrti poziv 6 4 10
Peti poziv 10 5 15

Detalje o array metodama možete naći na


https://javascript.info/array-methods
Funkcije
Funkcije
• Funkcije su osnovni “gradivni elementi programa” u jeziku
JS. Omogućuju da se kod poziva više puta bez ponovljenog
pisanja. Kao kada koristite matematičku formulu da nešto
sračunate više puta.
• Pri tome, kod mora da zna vrednosti koje se pojavljuju u
naredbama iz kojih se funkcija sastoji.
• Dakle, funkciji se moraju proslediti neke ulazne vrednosti i one
se prosleđuju (najčešće) putem liste parametara.
• Takođe, kao rezultat svog rada funkcija vraća jedan izlaz
• U JS-u funkcije su građani prvog reda što, između ostalog,
znači da se sa njima može raditi sve što se radi sa bilo
kojom drugom varijablom – funkcije mogu da se
prosleđuju drugim funkcijama kao ulazi i funkcija može da
vrati drugu funkciju kao izlaz.
Funkcije za interakciju sa programom
• Za obezbeđivanje interakcije JS nudi gotove, ugrađene funkcije alert,
prompt i confirm.
• alert(poruka) - ispisuje sadržaj promenljive poruka
alert("Hello");
• result = prompt(title, [default]) – ispisuje tekst i prihvata
ulaznu vrednost
title – tekst koji se ispisuje
default – pretpostavljena vrednost (neobavezno)
let age = prompt('Koliko Vam je godina?', 100);
alert(`Vama je ${age} godina!`);

• result = confirm(question) – prikazuje modalni prozor sa pitanjem


(question) i dva dugmeta: OK i Cancel. Ako se odabere OK, vrednost
promenljive result je true, ako se odabere Cancel, vrednost je false.
let isBoss = confirm("Da li ste Vi Gazda?");
alert( isBoss ); // true ako se odabere dugme OK, inače false
Varijable i funkcije
• “Regularne” vrednosti kao što su string i numerik
predstavljaju podatke koji se dodeljuju varijablama.
• Funkcija se može posmatrati kao akcija
opisana/predstavljena odgovarajućim izvornim
kodom.
• Izvorni kod nije ništa drugo do skup stringova, a
rezultat izvršavanja funkcije može da bude različitog
tipa
• Ti opisi se mogu prosleđivati među varijablama i
izvršavati kada mi to želimo.
• U JavaScript-u funkcija je varijabla
Primer: Varijabla u kojoj je zapisana funkcija
let funcs = []; // ovde se deklariše da je funcs niz
// Kreiraju se 3 funkcije i smeštaju se u niz funcs
for (let i = 0; i < 3; i++) {
funcs[i] = function() { // ovde se deklariše funkcija
// svaka treba da na konzoli ispiše svoju vrednost indeksa.
alert(" Ja sam " + " funcs [" + i + "]. Moj rezultat je: " + i);
};
}
for (let j = 0; j < 3; j++) {
// Sada pokrećemo svaku funkciju da uradi ono što treba
let vratio = funcs[j](); // moglo je da stoji i samo funcs[j]();
}
// Sada ispisujemo elemente niza funcs da vidimo šta je u njima
alert (" U funcs [0] piše : " + funcs[0])
alert (" U funcs [1] piše : " + funcs[1])
alert (" U funcs [2] piše : " + funcs[2])
Pisanje funkcija
• Videli smo da postoje gotove funkcije (ugrađene) koje
možemo da koristimo
• Funkcije, naravno, i sami možemo da pišemo.
• Da bi nešto bilo prepoznato kao funkcija, potrebno je
to nešto proglasiti/deklarisati kao funkciju.
• JavaScript ima tri načina deklarisanja funkcija
• Kanonička deklaracija (Function Declaration) -
naredba
• Funkcijski izraz (Function Expression) – deklaracija
unutar izraza
• Function konstruktor (o njemu ćemo kasnije)
Deklaracija funkcije: naredba function

Ključna Ime Lista parametara


reč funkcije (0 ili više)

function ime([parametri]) {
...telo... }
Telo funkcije
(šta funkcija radi)

function kaziZdravo() {
alert('Pozdrav svima!' );
}
Deklaracija funkcije: Funkcijski izraz
• Druga sintaksa za kreiranje funkcije je funkcijski izraz
( Function Expression).
• U ovoj sintaksi, funkcija se kreira i eksplicitno dodeljuje
varijbli - nije važno kako je funkcija definisana, ona je
samo vrednost smeštena u neku varijablu.
let kaziZdravo = function () {
alert('Pozdrav svima!' );
};
• Funkcijska naredba ima obavezan simbol ; na kraju, a u
funkcijskoj deklaraciji on nije obavezan.
Deklaracija funkcije: Imenovani funkcijski
izraz
• Funkcijski izraz deklariše anonimnu funkciju
(funkciju bez imena)
• Imenovani funkcijski izraz može da deklariše
neanonimnu funkciju (funkciju sa imenom)
• To ime je lokalno, važi samo u telu funkcije
• Omogućuje referisanje funkcije imenom u telu funkciji
(pogodno za rekurziju)
Imenovani funkcijski izraz:
primer
let math = {
'factit': function factorial(n) {
console.log(n)
if (n <= 1) {
return 1;
} return n * factorial(n - 1);
}
};
math.factit(3) //3;2;1;
Deklaracija funkcije i ime
• Varijabla kojoj se dodeli funkcijski izraz ima svoje ime.
• Ime funkcije se ne menja ako se ona dodeljuje
različitim varijablama.
• Ako se ime funkcije izostavi (neimenovani funkcijski
izraz) ime funkcije će biti isto kao ime varijable
(implicitno ime)
• Ako je ime funkcije prisutno, to će biti ime funkcije
(eksplicitno ime)
• Ovo važi i za streličastu sintaksu (o njoj ćemo kasnije)
Deklaracija funkcije i ime:
primer
var foo = function(){}
console.log(foo.name) // "foo "
var foo2 = foo
console.log(foo2.name) // "foo "
var bar = function baz() {}
console.log(bar.name) // "baz"
console.log(foo === foo2);// true
console.log(typeof baz); // undefined
console.log(bar === baz); // greška jer je
// baz == undefined
Pozivanje funkcije
• Da bi funkcija uradila svoj posao, ona se mora pozvati
• Funkcija se poziva navođenjem imena i vrednosti prametara (argumenti):
• Primer 1 (funkcija bez argumenata):
function showMessage() {
alert('Pozdrav svima!' );
}
showMessage(); // Pozdrav svima!

• Primer 2 (funkcija sa argumentima):


function showMessage(from, text) {
// argumenti: from, text
alert(from + ': ' + text);
}
showMessage('Ana', 'Zdravo, Soko!'); // (*) Ana: Zdravo, Soko!
showMessage('Soka', 'Zdravo, Ana! Šta ima? '); // (**) Soka: Zdravo, Ana! Šta ima?
• Kada se funkcija pozove (linije (*) i (**)), vrednosti argumenata se kopiraju u lokalne
varijable sa imenom parametara (u primeru 2 imena su from i text). Zatim ih funkcija
koristi.
Pozivanje funkcijskog izraza
let kaziZdravo = function() {
alert(' Pozdrav svima! ' );
};

let func = kaziZdravo;


alert (' Sadrzaj func: ' + func);
alert (' Sadrzaj kaziZdravo : ' + kaziZdravo);
func();
kaziZdravo();
• Šta će ovaj kod da ispiše?
Funkcija i varijable: lokalne varijable,
spoljašnje (globalne) varijable1
• Varijabla deklarisana unutar funkcije, vidljiva je samo unutar te funkcije:
function showMessage() {
let message = "Zdravo, ja sam JavaScript!"; // lokalna // varijabla
alert( message );
}
showMessage(); // Zdravo, ja sam JavaScript!
alert( message ); // <-- Uncaught ReferenceError: message is //
not defined at <anonymous>:6:8

• Funkcija može da pristupi i varijablama koje su deklarisane van nje (i da ih menja):


let userName = 'Jova';
function showMessage() {
userName = 'Pera'; // (1) izmenjena spoljašnja varijabla
let message = 'Zdravo, ' + userName;
alert(message);
}
alert( userName + ' pre poziva funkcije');
showMessage();
alert( userName+ ' vrednost je modifikovala funkcija' );
Funkcija: lokalne varijable, spoljašnje
(globalne) varijable2
• Ako postoje lokalna i spoljašnja varijabla sa istim imenom, lokalna varijabla
“zaklanja” spoljašnju:
let userName = 'Jova'; // deklarisana spoljašnja varijabla
function showMessage() {
let userName = "Pero"; // deklarisana lokalna varijabl istog imena
let message = 'Zdravo, ' + userName; // Zdravo, Pero
alert(message);
}
// funkcija će kreirati i koristiti sopstvenu
// varijablu userName
showMessage();
alert( userName ); // Jova, neizmenjeno,funkcija nije // pristupila
spoljašnjoj varijabli
• Varijable deklarisane van (bilo koje) funkcije (kao spoljašnja userName) zovu se
globalne varijable.
• Globalne varijable su vidljive iz svake funkcije (ako nisu “ zaklonjene ” lokalnim).
• Preporuka je da se minimizira korišćenje globalnih varijabli (zbog bočnih efekata – o
tome kasnije).
Funkcija: naredba return1
• Funkcija može (a ne mora) da vrati vrednost kao rezultat u kod koji je
poziva.
• Primer:
function sum(a, b) {
return a + b;
}

let result = sum(1, 2);


alert( result ); // Ispis: 3
• Naredba return može da bude bilo gde u funkciji.
• Naredba return ima dvojaku namenu funkciji.
• Da vrati vrednost
• Da kontroliše tok izvršavanja programa
• Kada se u izvršavanju dođe do return, kontrola toka se vraća kodu koji je
funkciju pozvao (na naredbu koja sledi poziv funkcije) i vraća se vrednost (u
primeru, ta vrednost se skladišti u promenljivu result).
Funkcija: naredba return2
• Return bez vrednosti rezultuje izlaskom iz funkcije:
function showMovie(age) {
if ( !(age > 12) ) {
alert (" You are under 12 ")
return;
}

alert( "Showing you the movie" ); // (*)


// ...
}
Funkcija: naredba return3
• Izlaz funkcije bez return ili sa praznim return je
nedefinisan (funkcija vraća vrednost undefined):
function doNothing() { /* bez return */ }
let fempty = doNothing ();
alert ('funkcija bez return je: ' + fempty)

function doNothing1() { /* sa praznim return*/


return;
}
let fempty1 = doNothing1 ();
alert ('funkcija sa praznim return je: ' +
fempty1)
Funkcija: naredba return4
• Nikada nemojte stavljati novi red između return i povratne vrednosti (; se
pretpostavlja iza return)
function sum(a, b) {
return

a + b;
}

let result = sum(1, 2);


alert( result ); // Ispis: undefined
• Višelinijski povratni izrazi se mogu stavljajti u zatvorenu malu zagradu:
return (
some + long + expression +
or + whatever * f(a) + f(b) )
Razlike između kanoničke deklaracije
function i funkcijskog izraza
1. Sintaksa
2. Kada JavaScript endžin kreira funkciju
3. Blokovsko dosezanje
Sintaksa
• JS sintaksa dozvoljava kanoničku deklaraciju samo u programu ili u telu funkcije.
1. Kanonička deklaracija: posebna naredba u kodu:
// Function Declaration
function sum(a, b) {
return a + b;
}
Nije dozvoljeno da se ova sintaksa koristi u konstruktu Block ({ ... }) u naredbama if,
while ili for.

2. Funkcijski izraz: reč function unutar izraza ili drugog sintaktičkog kostrukta. Na primer,
funkcija kreirana na desnoj strani “izraza dodele” =:
// Function Expression
let sum = function(a, b) {
return a + b;
};
Za ovakvu sintaksu dozvoljeno je korišćenje u konstruktu Block ({ ... }) u naredbama
if, while ili for.
Kada endžin kreira funkciju
• Funkcijski izraz se kreira kada se u izvršenju dođe do njega i
upotrebljiv je tek od tog momenta.
sayHi("Pero"); // greška – ovde još ne može!

let sayHi = function(name) { // (*) odavde može


alert( `Zdravo, ${name}` );
};
sayHi("Pero"); // ovo bi radilo!
• Kanonički deklarisana funkcija može se pozvati pre nego što je
definisana:
sayHi("Pero"); // Zdravo, Pero - ovo može!

function sayHi(name) {
alert( `Zdravo, ${name}` );
}
Blokovsko dosezanje1
• U režimu strict, kada je kanonička deklaracija unutar bloka
koda, vidljiva je unutar toga bloka a nije vidljiva izvan bloka:
"use strict";
let age = prompt("Koliko Vam je godina?", 18);
// Uslovno deklarisanje funkcije (u bloku)
if (age < 18) {
function welcome() {
alert("Hello!");
}
} else {
function welcome() {
alert("Greetings!");
}
}
// ...a poziva se izvan bloka
welcome(); // Greška: funkcija welcome nije // definisana
Blokovsko dosezanje2
"use strict";
let age = 16; // uzmimo 16 za primer
if (age < 18) {
welcome(); // \ (radi)
function welcome() { // |
alert("Ćao!"); // | Funkcija welcome je dostupna
} // | u bloku u kome je deklarisana
welcome(); // / (radi)
} else {
function welcome() {
alert("Dobar dan!");
}
}
// A ovde smo izvan bloka (crvene vitičaste zagrade),
// pa ne vidimo deklaraciju funkcije welcome koja je unutar bloka.
welcome(); // Zato dobijamo grešku: welcome is not defined
Kako to popraviti: funkcijski izraz sa let
deklaracijom i dodela u bloku
"use strict";
let age = prompt("Koliko Vam je godina?", 18);
let welcome; // deklaracija izvan bloka
if (age < 18) {
welcome = function() {
alert("Ćao!");
};
} else {
welcome = function() {
alert("Dobar dan!");
};
}
welcome(); // ovo sada radi
Kako to popraviti: funkcijski izraz drugi
način – sa uslovnim izrazom (?)

"use strict";
let age = prompt("Koliko Vam je godina?",
18);

let welcome = (age < 18) ?


function() { alert("Ćao!"); } :
function() { alert("Dobar dan!"); };

welcome(); // i sada radi


Koju deklaraciju koristiti?
• Funkcijski izraz samo u situacijama kad kanonička
deklaracija ne može da se upotrebi iz nekog razloga
i, posebno,  kada je potrebna uslovna deklaracija.
• Kanoničku deklaraciju u svim ostalim situacijama,
zato što
• Daje više slobode u organizaciji koda jer se može pozvati
pre pojave naredbe deklaracije u kodu.
• Čitljivija je.
Kontekst izvršavanja JS koda
Kontekst izvršavanja JS koda
• Cilj: da vam bude jasno šta JS endžin pokušava da uradi,
zašto se neke funkcije/varijable mogu koristiti pre nego
što se deklarišu, i kako se njihove vrednosti stvarno
određuju.
• Šta je kontekst izvršavanja
• Okruženje u kome se JS kod izvršava, može da bude
• Globalni kod — Pretpostavljeno okruženje gde se vaš kod prvi put
izvršava.
• Funkcijski kod — okruženje kada tok izvršavanja uđe u telo funkcije.
• Eval kod — Tekst koji treba da se izvrši unutar interne eval funkcije;
funkcija eval evaluira string u kome je kod koji će se izvršiti; smatra se
ENORMNIM BEZBEDNOSNIM RIZIKOM i NE PREPORUČUJE SE ZA
KORIŠĆENJE.
Kontekst izvršavanja: globalni i
funkcijski

Uvek može da postoji SAMO JEDAN globalni kontekst i PROIZVOLJAN (konačan) BROJ
funkcijskih konteksta
Koliko ima globalnih, a koliko funkcijskih konteksta ovde?
Evaluiranje koda: stek konteksta izvršavanja
• JavaScript endžin je u brauzeru implementiran kao jedna nit pa je i stek sa jednom
niti: To znači da se u brauzeru u jednom trenutku može dešavati samo jedna stvar
a ostale stvari se smeštaju u red čekanja koji se zove Stek izvršavanja:
Evaluiranje koda: stek konteksta izvršavanja
• Kada brauzer prvi put napuni skript, on ulazi u globalni
kontekst izvršavanja po pretpostavci.
• Kada se u kodu poziva funkcija, sekvenca toka programa ulazi
u pozvanu funkciju i pri tome kreira novi kontest izvršavanja
koji postavlja na vrh steka izvršavanja.
• Kada se pozove druga funkcija unutar tekuće funkcije,
dešava se ista stvar. Tok izvršavanja ulazi u unutrašnju
funkciju koja kreira novi kontekst izvršavanja koji se postavlja
na vrh postojećeg steka.
• Brauzer će uvek da izvršava tekući kontekst izvršavanja koji će
skidati sa vrha steka i vraćaće kontrolu kontekstu koji je ispod
u tekućem steku.
Stek konteksta izvršavanja:
primer
(function foo(i) {
alert(i)
if (i === 3) {
return;
}
else {
foo(++i);
}
}(0));
Stek konteksta izvršavanja:
kreiranje

EC4 kontekst - poziv foo (3)

EC3 kontekst - poziv foo (2)

EC2 kontekst - poziv foo (1)


EC1 kontekst - poziv foo (0)

Globalni kontekst
Stek konteksta izvršavanja:
ažuriranje steka pri izvršavanju

EC4 kontekst - poziv foo (3)

EC3 kontekst - poziv foo (2)

EC2 kontekst - poziv foo (1)


EC1 kontekst - poziv foo (0)

Globalni kontekst
Ključne stvari konteksta
izvršavanja
1. Jedna nit.
2. Sinhrono izvršavanje.
3. Jedan globalni kontekst.
4. Više (konačno mnogo) funkcijskih konteksta.
5. Svaki poziv funkcije kreira novi kontekst, čak i
rekurzivni poziv (kada funkcija poziva samu sebe).
JS kontekst izvršavanja: konceptualni
model
• Svaki kontekst izvršavanje se može konceptualno predstaviti
kao objekat sa tri svojstva (koja u i sama objekti):

executionContextObj = {
'scopeChain': { /* variableObject + svi
variableObject-i roditeljskih konteksta izvršenja
*/ },
'variableObject': { /* argumenti funkcije /
parametri, unutrašnje deklaracije varijabli i
funkcija */ },
'this': {}
}
Detalji JS konteksta
izvršavanja
• Svaki put kada se pozove funkcija, kreira se novi
kontekst izvršavanja
• Faza 1 - kreiranje: dešava se kada se funkcija
pozove, ali pre izvršavanja bilo kakvog koda unutar
funkcije
• Faza 2 – aktivacija/izvršavanje: dodeljuju se
vrednosti referenci funkcijama i kod se
interpretira/izvršava.
JS kontekst izvršavanja: Faza 1
1. Kreira se lanac dosezanja (Scope Chain).
2. Kreiraju se varijable, funkcije i argumenti.
3. Određuje se vrednost pokazivača this.
JS kontekst izvršavanja: Objekat
executionContextObj1

• Objekat executionContextObj se kreira kada se


funkcija pozove, ali pre stvarnog izvršavanja funkcije.
Dakle, kreira se u Fazi 1.
• Interpreter kreira ovaj objekat tako što skenira
funkciju tražeći parametre ili prosleđene argumente,
lokalne deklaracije funkcija, i lokalne deklaracije
varijabli.
• Rezultat ovog traženja je objekat variableObject
(VO) u objektu executionContextObj.
JS kontekst izvršavanja: Objekat
executionContextObj2
1. Nađi deo koda koji poziva funkciju.
2. Pre izvršavanja function koda, kreiraj kontekst
izvršenja.
• Inicijalizuj Scope Chain.
• Kreiraj variableObject (VO).
• Kreiraj argumentsObject, proveri kontekst po
parametrima, incijalizuj imena i vrednosti, i kreiraj kopiju
reference.
JS kontekst izvršavanja: Objekat
executionContextObj3
• Skeniraj kontekst u potrazi za deklaracijama funkcija:
• Za svaku nađenu funkciju kreiraj svojstvo u objektu
variableObject koje ima isto ime kao funkcija, i pokazivač
na funkciju u memoriji.
• Ako ime funkcije već postoji, pokazivač će biti prepisan.
• Skeniraj kontekst u potrazi za deklaracijama promenljivih:
• Za svaku nađenu deklaraciju varijable kreiraj svojstvo i objektu
variableObject sa imenom koje odgovara imenu varijable i
vrednost postavi na undefined.
• Ako ime varijable već postoji i objektu variableObject,
nemoj ništa da radiš i nastavi sa skeniranjem.
• Odredi vrednost za "this" unutar konteksta
JS kontekst izvršavanja: Faza 2
• U ovoj fazi se dodeljuju vrednosti referenci funkcijama i kod
se interpretira/izvršava.
• Pokreće se/ interpretira kod funkcije u kontekstu, i
dodeljuju se vrednosti varijablama kako se kod izvršava,
liniju po liniju.
Faza aktivacije/izvršenja koda: primer
function foo(i) {
var a = 'hello';
var b = function privateB() { };
function c() { }
}
foo(22);
Primer: Faza kreiranja za poziv foo(22)
fooExecutionContext = {
scopeChain: { ... },
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: Pokazivač na funkciju c()
a: undefined,
b: undefined
},
this: { ... }
}
Primer: Faza aktivacije/izvršavanja za poziv
foo(22)
fooExecutionContext = {
scopeChain: { ... },
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: Pokazivač na funkciju c()
a: 'hello',
b: Pokazivač na funkciju privateB()
},
this: { ... }
}
Hoisting (Podizanje)
• Postupak kojim se deklaracije varijabli i funkcija
postavljaju na vrh njihovog funkcijskog dosega.
Posledice su sledeće:
• Funkciji se može pristupiti pre nego što je deklarisana
• Ono što je deklarisano kao funkcija ostaje funkcija i ako
se (više puta) deklariše kao promenljiva
• Vrijable sa funkcijskom dodelom ostaju nedefinisane
A zašto su posledice hoisting-a takve?
(function() {
console.log(typeof foo); // pokazivač na //
funkciju
console.log(typeof bar); // undefined
var foo = 'hello',
bar = function() {
return 'world';
};
function foo() {
return 'hello';
}}());
A zašto su posledice hoisting-a takve?
1. Zašto možemo da pristupimo funkciji foo pre
nego što smo je deklarisali?
2. foo je dva puta deklarisana, zašto je foo
tipa function a nije undefined ili string?
3. Zašto je bar undefined ?
Ključna reč this
• Ključna reč this javlja se i u drugim jezicima i, u
većini slučajeva, ona pokazuje na novi objekat
kreiran pri instanciranju klase putem konstruktora.
• U JS-u ovakav koncept imamo u function
konstruktoru kada se on poziva korišćenjem ključne
reči new,
• U JS-u to nije jedino pravilo i ovde ključna reč this
može često da pokazuje na različite objekte iz
različitog konteksta izvršavanja.
JS this u globalnom kontekstu
• Kada god se koristi ključna reč this u globalnom
kontekstu (ne unutar funkcije), ona uvek pokazuje na
globalni objekat.
• Primer:
// globalni doseg

foo = 'abc';
alert(foo); // Ispis: abc

this.foo = 'def';
alert(foo); // Ispis: def
JS this u funkcijskom
kontekstu
var boat = {
size: 'normal',
boatInfo: function() {
alert(this === boat);
alert(this.size);
}
};
boat.boatInfo(); // Ispis: true, 'normal'

var bigBoat = {
size: 'big'
};
bigBoat.boatInfo = boat.boatInfo;
bigBoat.boatInfo(); // Ispis: false, 'big'
JS this u funkcijskom
kontekstu
• U JS-u vrednost ključne reči this unutar funkcije
nije statična, ona se određuje svaki put kada se
funkcija pozove, ali pre no što se stvarno izvrši kod
funkcije.
• Vrednost za ključnu reč this unutar funkcije u
stvari obezbeđuje roditeljski doseg u kome je
funkcija pozvana, i što je još važnije način na koji je
sintaksa funkcije napisana.
JS this u funkcijskom
kontekstu
• Pri pozivu funkcije, mora se obratiti pažnja na neposrednu levu
poziciju od zagrade “()”:
• Ako na levoj strani postoji referenca, vrednost this prosleđena funkcijskom
pozivu je pokazivač na objekat kome taj metod pripada.
• U protivnom, this pokazuje na globalni objekat.
function bar() {
alert(this);
}
bar(); // globalni - zato što metod bar() pripada globalnom //
objektu

var foo = {
baz: function() {
alert(this);
}
}
foo.baz(); // foo - zato što metod baz() pripada objektu foo
JS this : sintaksa poziva
• Ključna reč this može se menjati i u samoj funkciji:
var foo = {
baz: function() {
alert(this);
}
}
// prva sintaksa poziva
foo.baz(); // foo - zato što metod baz pripada objektu foo //
pri pozivu

// druga sintaksa poziva


var anotherBaz = foo.baz;
anotherBaz(); // global - zato što metod anotherBaz() pri
// pozivu pripada globalnom objektu, a ne //
objektu foo
JS this : ugnježdene funkcije
var anum = 0;
var foo = {
anum: 10,
baz: {
anum: 20,
bar: function() {
console.log(this.anum);
}
}
}
foo.baz.bar(); // Ispis:20 - jer je levo od () bar koji
// pripada objektu baz pri pozivu

var hello = foo.baz.bar;


hello(); // Ispis:0 - jer je levo od () hello koji //
pripada globalnom objektu pri pozivu
JS this : rukovalac
događajima
• this unutar rukovaoca događajima uvek pokazuje na
element na koji je trigerovan – sledeći kod će ukloniti
dugme Click to Remove Me:
<!DOCTYPE html>
<html>
<body>

<h2>The JavaScript <i>this</i> Keyword</h2>

<button onclick = "this.style.display = 'none'">


Click to Remove Me!</button>

</body>
</html>
JS this : eksplicitno
postavljanje
• Radi se pomoću funkcija call() i apply():
var person = {
fullName: function(city, country) {
return this.firstName + " " + this.lastName + "," + city + ","
+ country;
}
}
var person1 = {
firstName:"Pera",
lastName: "Perić"
}
var person2 = {
firstName:"Mika",
lastName: "Perić"
}
console.log (person.fullName.apply(person1, ["Beograd", "Srbija"]));
console.log (person.fullName.call(person2, "Novi Sad", "Srbija"));
Literatura za predavanje
1. Z. Konjović, Funkcionalno programiranje, Uvod,
slajdovi sa predavanja, dostupni na stranici predmeta
2. I. Kantor, PART 1 The JavaScript language, dostupno
na: https://javascript.info/
3. E. Elliot, Composing Software - An Exploration of
Functional Programming and Object Composition in
JavaScript, Leanpub, 2019, Poglavlje “A Functional
Programmer’s Introduction to JavaScript”
4. Debgging JavaScript in Chrome DevTools, dostupno na
https://developers.google.com/web/tools/chrome-dev
tools/javascript
(prevod na srpski Z. Konjović, dostupan na stranici
predmeta)
Sažetak
• JavaScript je programski jezik koji može da se koristi u brauzeru (klijentsko
skriptovanje), ali i van brauzera (skriptovanje na serverskoj strani)
• U JavaScript-u funkcije su građani prvog reda; mogu se prosleđivati
drugim funkcijama, mogu da vrate drugu funkciju kao povratnu vrednost,
mogu se komponovati, itd.
• Kontekst izvršavanja JavaScript-a ima sledeće bitne odlike:
• Jedna nit.
• Sinhrono izvršavanje.
• Jedan globalni kontekst.
• Više (konačno mnogo) funkcijskih konteksta.
• Svaki poziv funkcije (čak i rekurzivni) kreira novi kontekst.
• JavaScript je slabo tipiziran jezik, ali ipak ima 9 tipova (6 primitivnih i 3
strukturalna).
• U JavaScript-u, varijabli se može dodeliti podatak bilo kog tipa (nema tipa
varijable, ima samo tip podatka).
• U stvari, u JavaScript-u skoro sve je objekat
• U većini slučajeva, operatori/funkcije vrše implicitnu koerciju podataka i treba
znati pravila po kojima se to radi.

You might also like