Professional Documents
Culture Documents
Lect 02 - Funkcije U JS-u
Lect 02 - Funkcije U JS-u
programiranje
Školska 2020/21 godina
Zimski semestar
Blok 2
Lekcija 2: Funkcije u programskom
jeziku JavaScript – napredno korišćenje
Sadržaj1
• Streličasta sintaksa
• Prosleđivanje podataka
• Povratni poziv
• Rekurzija
• Sistem tipova i Hindley-Milner signatura tipa
Funkcije: streličasta sintaksa
Šta je streličasta sintaksa
• To je nova, veoma jednostavna i koncizna sintaksa za
kreiranje funkcija koja je često bolja od funkcijskih
izraza.
• Zove se “streličaste funkcije”, jer izgleda ovako:
let func = (arg1, arg2, ...argN) => izraz
• Gornja definicija je isto što i:
let func = function(arg1, arg2, ...argN) {
return izraz;
};
Streličasta sintaksa: konkretan primer
let sum = (a, b) => a + b;
alert( double1(3) ); // 6
alert( double2(3) ); // 6
welcome(); // ok radi
Višelinijske streličaste funkcije
• Za složenije stvari (n.pr., višestruke izraze ili naredbe),
streličasta sintaksa omogućuje da se oni zatvore u vitičastu
zagradu.
• U tom slučaju obavezna je eksplicitna return naredba:
let sum = (a, b) => { // vitičasta zagrada otvara
// višelinijsku funkciju
let result = a + b;
return result; // ako se koristi vitičasta zagrada,
// obavezan je eksplicitan "return"
};
alert( sum(1, 2) ); // 3
Streličaste funkcije NEMAJU this*
• Streličasta sintaksa je specifična po pitanju this pokazivača – prosto, streličaste
funkcije NEMAJU funkcijsko this.
• Ako se koristi this, ono se uzima spolja (this.title u primeru je isti kao i tihs
spoljašnje metode showList, dakle - : group.title):
"use strict";
let group = {
title: "Naša grupa",
students: ["Jova", "Pera", "Vera"],
showList() {
this.students.forEach(
student => alert(this.title + ': ' + student)
);
}
};
showList() {
this.students.forEach(function(student) {
alert(this.title + ': ' + student) // Greška: ne može se
učitati // svojstvo 'title' za undefined
});
}
};
group.showList(); // ne radi
Streličaste funkcije NEMAJU
this3
• Nepostojanje this prirodno uzrokuje još jedno
ograničenje: streličaste funkcije ne mogu se koristiti
kao konstruktori, odnosno ne mogu se pozvati sa
new.
• Postoji i suptilna razlika između streličaste funkcije
=> i regularne funkcije pozvane sa .bind(this):
• .bind(this) kreira “vezanu verziju” funkcije ().
• Streličasta sintaksa => ne kreira nikakvo vezivanje;
funkcija, prosto, nema this.
• Pretraga za this je ista kao i za regularnu varijablu, traži
se u spoljašnjem leksičkom dosegu.
Streličaste funkcije NEMAJU
this4
const module = {
x: 42,
getX: function() {
return this.x;
}
}
const unboundGetX = module.getX;
alert('poziv unbound: ' + unboundGetX()); // Poziv funkcije u
globalnom
// dosegu. Izlaz: undefined
const boundGetX = unboundGetX.bind(module);
alert('poziv bound: ' + boundGetX()); // Očekivani izlaz: 42
Streličaste funkcije nemaju varijablu
arguments*
• Varijabla arguments je zgodna za dekoratorski obrazac kada treba
forvardovati poziv sa tekućim this i arguments.
• Na primer, defer(f, ms) prima funkciju f i vraća vraper oko
nje koji će odložiti njeno izvršavanje za ms milisekundi:
function defer(f, ms) {
return function() {
setTimeout(() => f.apply(this, arguments), ms)
};
}
function kaziZdravo(who) {
alert('Zdravo, ' + who);
}
let kaziZdravoOdlozi = defer(kaziZdravo, 500);
kaziZdravoOdlozi("Jovo"); // Nakon 5 sec ispis: //
Zdravo, Jovo
Funkcije: Prosleđivanje
podataka
Prosleđivanje podataka:
sadržaj
• Prametri/argumenti
• Pretpostavljeni parametri
• Rest parametri
• Spread sintaksa
• Doseg varijabli
• Globalni objekat
• Funkcijski objekat, imenovani funkcijski izraz
• new function konstrukt
• Povratni poziv, raspoređivanje izvršavanja: setTimeout i
setInterval
• Dekoratori i forvardovanje, call/apply
• Vezivanje (binding) funkcije
• Kuriranje
• Parcijalna aplikacija
Prosleđivanje podataka funkciji:
parametri/argumenti
• Funkcija prima ulazne podatke putem
parametara/argumenata
• Parametri se navode u deklaraciji funkcije
• Argumenti se navode pri pozivanju funkcije
• JavaScript je vrlo fleksibilna po pitanju broja
(ponekad i tipa) parametara i argumenata
Fleksibilnost parametri/argumenti: situacije
i posledice
• Fleksibilnost u odnosu broja parametara i
argumenata rezultuje sledećim situacijama:
• Da se prosledi baš onoliko argumenata koliko ima
parametara – ovde je sve “po pravilima službe”
• Da se prosledi više argumenta nego što ima parametara
– ovde se nešto mora uraditi sa “viškom” argumenata
• Da se prosledi manje argumenata nego što ima
parametara – ovde se mora rešiti pitanje “manjka”
argumenata
• Da treba prosleđivati različit broj argumenata pri
različitim pozivima – ovde se mora smisliti nešto što će u
sebi da čuva argumente za svaki pojedinačni poziv.
Prosleđivanje podataka: višak argumenata
• Argumenti koji su “višak” se, prosto, ignorišu.
• Primaju se 2 argumenta a prosleđeno je 5
argumenata (vratiće vrednost 3, a argumenti 3, 4, i
5 će biti ignorisani):
function sum(a, b) {
return a + b;
}
return sum;
}
window.currentUser = {
name: "John"
};
f();
I objektne metode imaju ime*
let user = {
kaziZdravo() {
// ...
},
kaziCiao: function() {
// ...
}
}
alert(user.kaziZdravo.name); //Ispis:kaziZdravo
alert(user.kaziCiao.name); // Ispis:kaziCiao
Ali ima i situacija kada se ime ne može
odrediti
// funkcija kreirana unutar niza
let arr = [function() {}];
alert(f1.length); // Ispis: 1
alert(f2.length); // Ispis: 2
alert(many.length); // Ispis: 2 (rest
// parametri se ne
// broje
Svojstvo length: jedno često korišćenje
• Za introspekciju (u računarstvu, introspekcija tipa je sposobnost
ispitivanja tipa ili svojstava objekta u vreme izvršenja programa) u
funkcijama koje operišu nad drugim funkcijama.
• Primer:
Funkcija ask prima argument question kao pitanje i proizvoljan
broj hendlerskih funkcija koje će se pozivati (po potrebi). Kada
korisnik da svoj odgovor, funkcija poziva hendlere. Mogu se
proslediti dve vrste hendlera:
• Funkcija bez argumenata koja se poziva samo ako korisnik da potvrdan
odgovor.
• Funkcija sa argumentima koja se poziva u svakom slučaju i vraća odgovor.
Da bi se hendler pozvao na ispravan način, može se ispitati
svojstvo handler.length.
Svojstvo length: kod za primer
korišćenja*
function ask(question, ...handlers) {
let isYes = confirm(question);
for(let handler of handlers) {
if (handler.length == 0) {
if (isYes) handler();
} else {
handler(isYes);
}
}
}
// za potvrdan odgovor se pozivaju oba hendlera
// za negativan odgovor se poziva samo drugi hendler
ask("Pitanje?",
() => alert('Potvrdili ste'),
result => alert(result));
Dodavanje svojstava function objektu
• Ako želimo da brojimo pozive funkcije, to može da se uradi
dodavanjem svojstva brojac:
function kaziZdravo() {
alert("Zdravo!");
// hajde da brojimo koliko se puta izvršavamo
kaziZdravo.brojac++;
}
kaziZdravo.brojac = 0; // početna vrednost
counter.count = 10;
alert( counter() ); // 10
Prosleđivanje podataka: new Function
konstrukt
• Ovo je još jedan način za kreiranje funkcije
• Ne koristi se previše često, ali po nekad ne može
drugačije
• Glavna razlika od drugih načina koje smo videli je u
tome što se funkcija kreira doslovce iz stringa koji se
prosleđuje u vreme izvršavanja.
• Sve prethodno viđene deklaracije zahtevaju da funkcijski
kod bude zapisan u okviru skripta.
• new Function omogućuje da se svaki string “pretoči” u
funkciju.
new Function konstrukt: sintaksa*
• Sintaksa je sledeća:
let func = new Function ([arg1, arg2, ...argN], functionBody);
• Primer:
let sum = new Function('a', 'b', 'return a + b');
alert( sum(1, 2) ); // Ispis: 3
• Primer:
let str = ... Primljeno dinamički sa servera ...
function showOk() {
alert("Saglasili ste se." );
}
function showCancel() {
alert("Otkazali ste." );
}
function processUserInput(callback) {
var name = prompt('Molim, unesite ime');
callback(name);
}
processUserInput(greeting);
Funkcije: vremensko raspoređivanje
izvršavanja
• Moguće je funkciju izvršiti odloženo – ne odmah, već nakon
određenog vremena.
• To se zove raspoređivanje poziva (“scheduling a call”).
• Za to postoje dve metode:
• setTimeout omogućuje da se funkcija pokrene jednom nakon
zadatog vremenskog intervala.
• setInterval omogućuje da se funkcija izvršava ponovljeno počevši
od nekog vremenskog intervala i kontinuirano ponavljajući izvršavanje
sa učestalošću određenom tim vremenskim intervalom.
• Ove metode nisu deo JavaScript specifikacije, ali većina
implementacija ima interni raspoređivač i pruža te metode.
Posebno, metode su podržane u baruzerima i okruženju
Node.js.
Funkcije: setTimeout
• Sintaksa
let timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)
• Parametri:
• func|code - Funkcija ili string koda za izvršavanje. Obično je to
funkcija. Iz istorijskih razloga može se proslediti i
string koda, ali se to ne preporučuje.
• delay - vreme do pokretanja, u milisekundama;
pretpostavljena vrednost 0 (ako je 0, ne znači da
će se baš odmah izvršiti).
• arg1, arg2, ... - Argumenti funkcije (nije podržano u IE9-)
setTimeout: primer*
function kaziZdravo(fraza, ko) {
alert( fraza + ', ' + ko );
}
• Primer:
// ponavljaj svakih 2 sekunde
let timerId = setInterval(() => alert('tick'), 2000);
let f = user.kaziZdravo;
setTimeout(f, 1000); // Zdravo, undefined!
Povezivanje (binding): Kada se gubi this
• Kada se metod prosledi odvojeno od objekta – gubi se
this.
let user = {
firstName: "Jovo",
kaziZdravo() {
alert(`Zdravo, ${this.firstName}!`);
}
};
// Ovde je setTimeout dobila user.kaziZdravo odvojeno od objekta.
let f = user.kaziZdravo;
setTimeout(f, 1000); // izgubljen user kontekst
Povezivanje (binding): Zašto se gubi this
• Metoda setTimeout u brauzeru je malo specijalna:
ona postavlja this = window za poziv funkcije(za
Node.js, to postaje timer objekat).
• Dakle, za this.firstName pokušava se dobavljanje
window.firstName, što ne postoji pa this
(uobičajeno u ovakvim slučajevima) postaje
undefined.
• Zadatak je tipičan – želimo da prosledimo metodu
objekta negde drugde (ovde – raspoređivaču) gde će
ona biti pozvana. Kako obezbediti da se ona pozove u
pravom kontekstu?
Gubljenje this: kako rešiti?
• Rešenje 1: Wraper – omotačka funkcija koja prima
kontekst (objekat) i zatim normalno poziva metodu.
• Rešenje 2: bind metoda – funkcije imaju ugrađenu
metodu koja se zove bind i koja rešava ovaj
problem
Rešenje 1: Omotačka funkcija
let user = {
firstName: „Jovo",
kaziZdravo() {
alert(`Zdravo, ${this.firstName}!`);
}
};
setTimeout(function() {
user.sayHi(); // Zdravo, Jovo!
}, 1000);
Rešenje 1: potencijalni problem
• Šta ako se pre trigerovanja funkcije setTimeout (a
ima 1 sekunda kašnjenja!) promeni vrednost user?
let user = {
firstName: "Jovo",
kaziZdravo() {
alert(`Zdravo, ${this.firstName}!`);
}
};
setTimeout(() => user.kaziZdravo(), 1000);
// ...vrednost promenljive user se promeni u toku te sekunde
user = {
kaziZdravo() { alert("Drugi korisnik u setTimeout!"); }
};
// Ispis: Drugi korisnik u setTimeout!
Rešenje 2: Ugrađena metoda bind*
return result;
}
list 1 2 null
secondList 3 3 null