webdevlpr

Funkcije su objekti u JavaScript-u.

Postoji osam osnovnih tipova podataka u JavaScript-u. Sedam od njih su primitivni tipovi podataka (poput brojeva, teksta itd.) i osmi je neprimitivni tip — objekat, a funkcije su prvoklasni objekti koji se mogu pozvati sa zagradama () kako bi izvrsili naredbu / akciju. Drugim rijecima, moguce je raditi sa funkcijama kao sa objektima.

function sayHi() {
console.log('Greetings');
}

sayHi(); // Greetings
console.log(typeof sayHi); // function
console.log(sayHi instanceof Object); // true

Svojstva objekta

Funkcija moze biti korisna sama po sebi i nositi bezbroj dodatnih funkcionalnosti u svojstvima.

Jedna od osobina koju funkcije dijele sa objektima su svojstva (key: value parovi i metode) koje im se mogu kreirati, modifikovati i brisati. Za one koji nisu upoznati sa ovim tipom podatka, ispod je definisan objekat sa jednim key: value parom, da bi izvan objekta pristupili tom svojstvu i izmjenili njegovu vrijednost.

let sum = {
current: 0;
};

sum.current = 3 + 5; // modifikuje vrijednost

A ovako izgleda funkcija sum() kojoj smo dodijelili isto svojstvo koristeci tacku sum.current.

function sum(a, b) {
sum.current = a + b; // sum.current nije varijabla
}

sum.current = 0; // definisano svojstvo objekta funkcije

sum(3, 5);
console.dir(sum); // prikazuje svojstva objekta

Rezultat koda u konzoli:

Rezultat koda u konzoli.
Prikaz sum() svojstava.

Kako je ona poseban tip objekta, dodijeljena su joj neka korisna svojstva koji drugi tipovi objekata nemaju. U konzoli su prikazani sa tamnijim slovima (arguments, caller, length, name itd.). Current svojstvo nije doslo sa funkcijom, definisano je u kodu.

Duzina parametara funkcije

length svojstvo se odnosi na definisan broj parametara koje funkcija prihvata:

function add(a, b) {
return a + b;
}

console.log(add.length); // 2

Kontekstualno ime

Name svojstvo, kako ocekujemo, daje pristup imenu funkcije.

function add(a, b) {
return a + b;
}

console.log(add.name); // add

Cak i kada se radi o varijabli kojoj je po kreiranju dodjeljena funkcija bez imena, svojstvo name ce zakljuciti ime iz konteksta.

let add = function(a, b) {
return a + b;
};

console.log(add.name); // add

Koje ime ce imati izraz funkcije (function expression) ako varijabli dodijelimo funkciju sa imenom?

let add = function calculate(a, b) {
return a + b;
};

console.log(add.name); // calculate

Ili ako funkcija nije dodijeljena varijabli i nema ime?

let func = [function() {}];

console.log(func[0].name); // *crickets chirping*
console.dir(func[0]);

// ƒ anonymous()
// arguments: null
// caller: null
// length: 0
// name: ""
// prototype: {constructor: ƒ}

Prioritet uvijek ima ime deklarisano nakon kljucne rijeci function, a ako ga uopste nema - onda se radi o anonimnoj funkciji sa name: "" (vrijednost je prazan string).

Imenovani funkcijski izrazi

Deklaracija funkcije (function declaration) uvijek pocinje sa function imeFunkcije() {...}, dok izraz funkcije (function expression) omogucava kreiranje nove funkcije unutar bilo kog izraza.

let func = function() {
// ...
};

Ovo je jedan primjer izraza funkcije jer se kreiranje funkcije desava u sklopu izraza — kroz dodjeljivanje anonimne funkcije varijabli. Izrazi zavrsavaju sa ; da bi kompajler razlikovao i odvojio izraze. Funkcija nema ime i name svojstvo ove funkcije je izvuceno iz konteksta (varijable func).

Da bi kreirali imenovani funkcijski izraz (Named Function Expression), varijabli func treba dodijeliti imenovanu funkciju:

let func = function read() {
// ..
}

console.log(func.name); // read

Kljucna stvar kod ovakve funkcije je da ona rezervise read identifikator za unutrasnji opseg i predstavlja pouzdan nacin za pozivanje sebe. Pouzdan je jer se taj identifikator ne moze izmjeniti nakon kreiranja funkcije, cak i ako se promijeni ime varijable. Ali read identifikator je pogodan za interno pozivanje sebe jer nije dostupan izvan funkcije, dok je func ime varijable dostupno unutar i izvan funkcije i njeno ime se moze izmjeniti.

let func = function read(quote) {
if (quote) {
console.log(quote);
} else {
read("Lorem ipsum dolor sit amet.");
// poziva sebe sa argumentom
}
};

func(); // Lorem ipsum...

read();
// Uncaught ReferenceError: read is not defined

Metode funkcije

Kao i objekti, funkcije mogu imati metode, a neke su predefinisane (console.dir(add)):

  ƒ add(a, b)
  arguments: null
  caller: null
  length: 2
  name: "add"
  [[Prototype]]: ƒ ()
    bind: ƒ bind()
    toString: ƒ toString()
    Symbol(Symbol.hasInstance): ƒ [Symbol.hasInstance]()
    get arguments: ƒ ()
    ...

func.toString() u konzoli vraca tijelo funkcije kao string.

Metoda toString() primjenjena na funkciji u konzoli.
Metoda toString() primjenjena na funkciji u konzoli.

Mnoge JavaScript biblioteke koriste ovu osobinu funkcija. jQuery biblioteka ima glavnu funkciju $ i ostale pomocne funkcije koje se vezu za nju. Na ovaj nacin se izbjegava preopterecenje globalnog prostora i konflikti imenovanja varijabli i funkcija.

Prosljedjivanje po referenci

One se prosljedjuju po referenci (pass by reference) kao i drugi objekti (array i object), dok se primitivni tipovi podataka (svi ostali) prosljedjuju po vrijednosti. Ovdje je detaljnije objasnjeno sta to znaci i kako se razlikuju.

U sustini, kada varijabli dodijelimo funkciju, ona ne dobija kopiju funkcije, vec referencu koja pokazuje na mjesto u memoriji gdje se ona nalazi. Tako dvije varijable imaju razlicita imena iako pristupaju istoj funkciji.

let book = function() {
// ...
}

var book2 = book;
book.author = "Ana";

console.log(book.author); // Ana
console.log(book2.author); // Ana

book2.author = "Vana";

console.log(book.author); // Vana
console.log(book2.author); // Vana

Funkcije se mogu prosljedjivati kao argumenti drugim funkcijama. I u ovom slucaju argumenti su referenca na funkciju, ne njena kopija.

function calculate(callback, x, y) {
let result = callback(x, y);
console.log(result);
}

function add(x, y) {
return x + y;
}

function multiply(x, y) {
return x * y;
}

calculate(add, 2, 3); // => 5
calculate(multiply, 2, 3); // => 6