Uvod
Često se pogrešno misli da je JavaScript interpretiran programski jezik koji se interpretira tj. direktno izvršava red po red bez prethodnog kompajliranja (prevodjenja jezika na mašinski jezik). Medjutim JavaScript je programski jezik koji se kompajlira pri izvršenju koda (“on the fly“). JavaScript kod se kompajlira svaki put pri izvršenju koda, tako da kvalitet i brzina odziva JavaScripta kao jezika najviše zavisi od efikasnosti Javascript engine koji vrši kompajliranje.
Hoisting promenjive
Deklarisanje i definisanje promenjive su dva različita postupka. “Deklaracija promenjive” je postupak stvaranja promenjive, tada se definiše ime promenjive i obezbedjuje prostor za njeno skladištenje, dok je “definisanje promenjive” postupak kada se promenjivoj dodeljuje neka vrednost koja se smešta u prostor za skladištenje.
Hoisting deklarisane promenjive
Deklaracija promenjive se izvršava pri kompajliranju koda, što znači da se pre izvršenja i jednog reda koda prvo deklarišu promenjive. U okviru JavaScript-a postoji specijalna rezervisana reč “var” koja se koristi da deklariše promenjivu. Pošto se deklaracija promenjive dešava pre izvršavanja bilo kog dela koda sledi da je to isto kao da su promenjive deklarisane na početku koda, ovaj princip se naziva “hoisting” (srp. dizanje).
1 2 3 4 5 6 7 8 9 10 11 12 |
function nekaFunkcija (){ // neki kod } var a = 5; Stvarni izgled koda: var a; function nekaFunkcija (){ // neki kod } a = 5; |
Tako da u slučaju da se pozove neka promenjiva pre nego što dodje do dela koda gde je u deklarisana i definisana (ustvari ona je već deklarisana) neće izbaciti grešku ali će izbaciti da vrednost te promenjive nije još definisana.
1 2 |
console.log(x); // ne daje grešku nego samo "undefined" var x = 5; |
Primer
Na sledećem primeru će biti objašnjen slučaj vezan za for petlju, na koji treba obratiti pažnju da bi se izbegle greške.
1 2 3 4 5 |
function f() { for (var i = 0; i < c; i++) { ... } } |
Prethodni primer posle hoistinga promenjive i izgleda ovako:
1 2 3 4 5 6 |
function f() { var i; for (i = 0; i < c; i++) { ... } } |
Tako da treba obratiti pažnju da se ne unutar funkcije ne deklariše promenjiva sa istim nazivom.
Šta se dešava sa nedeklarisanim promenjivama?
Ukoliko se ne koristi rezervisana reč “var”, promenjiva će biti deklarisana “on the fly” u globalnom scope i biće dostupana iz celog programskog koda. Ova radnja se izvodi tek u vreme izvršavanja koda, što je nakon kompajliranja i trenutka kada se izvršava “dizanje” promenjivih na vrh koda (hoisting). Stoga pošto je prošao trenutak kada se vrši hoisting, promenjiva neće biti dignuta na početak koda. Tako da ukoliko se kao u sledećem primeru pozove takva promenjiva ranije u kodu izbaciće grešku!
1 2 |
console.log(x); // Izbacuje error: x is not defined x = 5; |
Primer
1 2 3 4 |
function testScope() { unutrasnja = "test"; } console.log(unutrasnja); // Izlaz: test (dostupna je iz globalnog scope) |
Pošto nije deklarisana sa var deklariše se u globalnom scope pa je dostupna iz celog programa!.
Primer
1 2 3 4 5 6 7 8 |
var test = "vrednost iz globalnog domena" function testScope() { test = "Vrednost iz lokalnog domena"; console.log(test); } console.log(test); // Izlaz: vrednost iz globalnog domena testScope(); // Izlaz: Vrednost iz lokalnog domena console.log(test); // Izlaz: Vrednost iz lokalnog domena |
Pri pozivanju funkcije testScope() iz globalnog domena, poziva se i globalna promenjiva X, nakon “obrade” u funkciji se njoj dodeljuje nova vrednost “Vrednost iz lokalnog domena.
Hoisting funkcije
Deklaracija funkcije se takodje odvija pre izvršenja koda tj. pri kompajliranju, stoga isto kao kod promenjivih može se smatrati da su pisane na početku koda. Postupak dizanja deklarisanih funkcija (cela funkcija sa svim kodom) na početak koda naziva hoisting funkcije. Iz tog razloga funkciju možemo da napišemo u dnu koda i da je pozivamo sa vrha bez izbacivanja greške kao u sledećem primeru:
1 2 3 4 |
test(); // Izlaz: bez greske (jer je funkcija već hoistovana) function test(){ console.log("bez greske"); } |
Treba naglasiti da se hositing funkcija odvija pre hoisting-a promenjivih, tj.
Primer
Izlaz test() funkcije u sledećem primeru biti nejasno na prvi pogled.
1 2 3 4 5 6 7 |
var test = function (){ console.log("iz function expression"); } function test(){ console.log("iz deklarisane funkcije"); } test(); // Izlaz: iz function expression |
Prethodni primer će sam sebe objasniti ako se kod napiše kada je hoistovan:
1 2 3 4 5 6 7 8 |
function test(){ console.log("iz deklarisane funkcije"); } // prvo se diže cela funkcija var test; // zatim se diže deklaracija promenjive var test = function (){ console.log("iz function expression"); } // ostalo na istom mestu u kodu test(); // ostalo na istom mestu u kodu |
Pošto definisanje funkcije test preko promenjive dolazi posle hoistovane fukcije njen sadržaj “gazi” prethodni pa je krajnji izlaz: “iz function expression”.
OBJAŠENJENJE:
Razlika izmedju pojmova “deklaracijie funkcije” i “function expression” se najlakše objašnjava na primerima:
1 2 |
function test() {} // function declaration var test = function() {}; // function expression |
Osnovna razlika je u poziciji specijalne reči function unutar izraza: ukoliko je reč “function” na početku izraza onda je u pitanju deklaracija, u ostalim slučajevima je “function expression”.
Ugnježdjena funkcija se ne diže
Treba obratiti pažnju na pravilo da “ugnježdjena” funkcija unutar neke druge se ne može dići na početak svoga parent scopa tj. funkcije se ne hoistuju unutar child domena:
1 2 3 4 5 6 7 8 9 |
function f() { { g(); // Vraća error je g() funkcija nije definisana pre ove linije function g() { ... } } } |
Poštovani, ovaj deo sa “ugnježdjena funkcija” je izgleda promenjen od strane ECMA. Upravo sam testirao, i hoisting se odnosi i na “ugnježdjenje funkcije”, tj nije izbacio nikakav error, već se funkcija normalno izvršila. Čak i pod use strict-om.
S’ poštovanjem Dejan Marković.
Mislim da je tako od verzije EcmaScript 6.