You are on page 1of 8

POGLAVLJE 27

Sintaksa

Ako ste pisali JS kd barem neko vreme, vrlo je verovatno da vam je sintaksa prilino po-
znata. Svakako postoje mnoge posebnosti, ali u optem sluaju, to je prilino smislena i
lako razumljiva sintaksa koja ima mnogo slinosti s drugim jezicima.
Meutim, ES6 uvodi prilian broj novih sintaksnih oblika na koje se treba navii. U
ovom poglavlju emo ih navesti pojedinano da bismo razmotrili ta sve postoji.

U vreme pisanja ove knjige, neke od mogunosti koje razmatramo ve su imple-


mentirane u raznim itaima veba (Firefox, Chrome itd.), dok su druge implemen-
tirane samo delimino, a mnoge uopte nisu implementirane. Vae lino iskustvo
koje ete imati ako isprobate primere iz ove knjige moe biti drugaije od onog to
je ovde opisano. U takvim sluajevima, isprobajte ih u verziji s transpajlerima, po-
to te alatke pokrivaju navei deo novih ES6 mogunosti.
ES6Fiddle (http://www.es6fiddle.net/) odlino i lako upotrebljivo igralite za isprobavanje
ES6 koda, kao to je veb REPL za transpajler Babel (http://babeljs.io/repl/).

Deklaracije iji je opseg vidljivosti blok


Verovatno znate da je u JavaScriptu osnovna jedinica za opseg vidljivosti promenljivih
oduvek bila funkcija. Ako vam je trebalo da formirate opseg vidljivosti ija je veliina
odreeni blok koda, najuobiajeniji nain da to uradite osim korienja standardne
funkcije bio je upotreba funkcijskog izraza za trenutno izvravanje (IIFE). Na primer:
var a = 2;

(function IIFE(){
var a = 3;
console.log( a ); // 3
})();

console.log( a ); // 2

646
Deklarisanje promenljivih pomou rezervisane rei let
Meutim, sada moemo zadavati deklaracije promenljivih koje su vezane za odreeni
blok, to se (nimalo iznenaujue) zove opseg vidljivosti veliine bloka. To znai da nam
treba samo par { .. } kako bismo definisali opseg vidljivosti. Umesto rezervisane rei var,
koja uvek deklarie promenljive iji je opseg vidljivosti okruujua funkcija (ili globalni
opseg, ako su deklarisane na najviem nivou), zadajte rezervisanu re let:
var a = 2;

{
let a = 3;
console.log( a ); // 3
}

console.log( a ); // 2
U JS-u dosad nije bila mnogo uobiajena upotreba samostalnog bloka { .. }, ali je to
oduvek bila ispravna sintaksa. Taj model e odmah prepoznati programeri iz drugih jezi-
ka u kojima se moe definisati opseg vidljivosti veliine bloka.
Verujem da je to najbolji nain da se deklariu promenljive iji opseg vidljivosti je
blok, unutar vlastitog bloka { .. }. Osim toga, trebalo bi da let deklaraciju (ili deklara-
cije) uvek postavite na poetak tog bloka. Ako treba da deklariete vie promenljivih, pre-
poruio bih vam da zadate samo jedan let.
to se stila pisanja tie, ja ak najradije piem let u istom redu gde je poetna zagra-
da {, kako bih jasnije istakao da je svrha tog bloka samo deklarisanje opsega vidljivosti
tih promenljivih.
{ let a = 2, b, c;
// ..
}
To e izgledati udno i verovatno se nee slagati s preporukama iz veeg dela literatu-
re o ES6. Ali imam dobre razloge za svoju uvrnutost.
Postoji jo jedan eksperimentalan (nestandardizovan) oblik let deklaracije nazvan
let blok, koji izgleda ovako:
let (a = 2, b, c) {
// ..
}
Taj oblik je ono to zovem eksplicitni opseg vidljivosti veliine bloka, dok je oblik let
.. deklaracije koji preslikava oblik var deklaracije vie implicitan, poto nije najjasnije
vidljivo kojem paru { .. } pripada. Programeri uglavnom smatraju da je upotreba ekspli-
citnih mehanizama ipak preporuljivija od implicitnih mehanizama, a ja tvrdim da je ovo
jedan od takvih sluajeva.
Ako uporedite prethodna dva oblika, oni su vrlo slini, a po mom miljenju, oba po
stilu pripadaju eksplicitnom zadavanju opsega vidljivosti veliine bloka. Naalost, oblik

Deklaracije iji je opseg vidljivosti blok | 647


let (..) { .. }, najeksplicitnija opcija, nije prihvaen u verziji ES6. To e se moda pro-
meniti u nekoj verziji posle ES6, ali mislim da nam je prva opcija zasad najbolja.
Da biste bolje shvatili implicitnu prirodu let .. deklaracija, razmotrite sledee slua-
jeve upotrebe:
let a = 2;

if (a > 1) {
let b = a * 3;
console.log( b ); // 6

for (let i = a; i <= b; i++) {


let j = i + 10;
console.log( j );
}
// 12 13 14 15 16

let c = a + b;
console.log( c ); // 8
}
Brza provera, bez gledanja u primer: koja promenljiva (ili promenljive) postoji samo
unutar iskaza if, a koja promenljiva (ili promenljive) postoji samo unutar petlje for?
Odgovori: iskaz if sadri promenljive b i c iji je opseg vidljivosti blok iskaza if, a pet-
lja for sadri promenljive i i j iji je opseg vidljivosti blok petlje.
Da li ste morali da razmiljate izvesno vreme? Iznenauje li vas to to promenljiva i
nije dodata u opseg vidljivosti bloka svog okruujeeg iskaza if? Razlog te mentalne pa-
uze i pitanja ja to zovem mentalna taksa potie od injenice da taj let mehanizam
ne samo to je za nas novina, nego je i implicitan.
injenica da se deklaracija let c = .. nalazi tako duboko unutar opsega vidljivosti
uvodi i odreenu opasnost. Za razliku od promenljivih koje deklariete na tradicionalan
nain pomou var, koje vae u celom okruujuem opsegu vidljivosti funkcije u kojem
su deklarisane, let deklaracije se dodaju u opseg vidljivosti svog bloka, ali se ne inicijali-
zuju sve do mesta gde se pojavljuju u bloku.
Pristupanje promenljivoj koja je deklarisana pomou let pre njene let .. deklaraci-
je/inicijalizacije prouzrokuje greku, dok je za var deklaracije taj redosled nevaan (osim
u pogledu stila).
Razmotrite sledee:
{
console.log( a ); // undefined
console.log( b ); // ReferenceError!

var a;
let b;
}

648|Poglavlje 27Sintaksa
Ta greka ReferenceError zbog preranog pristupanja promenljivoj koja je de-
klarisana pomou let tehniki se zove TDZ (Temporal Dead Zone privreme-
na mrtva zona) greka pristupate promenljivoj koja je deklarisana ali jo nije
inicijalizovana. To nije jedini sluaj kada viamo TDZ greke one se u ES6 po-
javljuju na vie mesta. Osim toga, imajte u vidu da inicijalizovana ne znai
eksplicitno dodeljivanje vrednosti u kodu, poto je let b; potpuno ispravna sintaksa. Budui
da se za promenljivu kojoj u trenutku deklarisanja nije dodeljena nikakva vrednost podrazu-
meva da joj je dodeljena vrednost undefined, iskaz let b; isto je to i let b = unde fined;.
Bez obzira na to da li je inicijalizujete eksplicitno ili implicitno, promenljivoj b ne moete pri-
stupati dok se ne izvri iskaz let b.

Poslednja zamka: operator typeof se sa TDZ promenljivama ponaa drugaije nego s


nedeklarisanim (ili deklarisanim!) promenljivama. Na primer:
{
// `a` nije deklarisana
if (typeof a === undefined) {
console.log( cool );
}

// `b` je deklarisana, ali u svojoj TDZ


if (typeof b === undefined) { // ReferenceError!
// ..
}

// ..

let b;
}
Poto promenljiva a nije deklarisana, upotreba operatora typeof jedini je bezbedan
nain da proverimo postoji li ona ili ne. Ali, izraz typeof b prouzrokuje TDZ greku zato
to duboko nie u kodu sluajno imamo deklaraciju let b. Uh.
Sada bi trebalo da bude jasnije zbog ega insistiram da se sve let deklaracije nalaze
na poetku svojih opsega vidljivosti. Tako se potpuno izbegavaju nenamerne greke zbog
preranog pristupanja promenljivoj. Osim toga, tako postaje eksplicitnije kada pogleda-
te poetak bloka, svakog bloka koje promenljive on sadri.
Vai blokovi (iskazi if, petlje while itd.) ne moraju da dele svoje izvorno ponaanje s
ponaanjem u vezi sa opsegom vidljivosti.
Ta vaa eksplicitnost, koju ete samo morati da disciplinovano odravate, na dui rok
e vas potedeti mnogih glavobolja.

Vie informacija o deklarisanju promenljivih pomou let i opsega vidljivosti


veliine bloka nai ete u drugom delu ove knjige Opseg vidljivosti i ograde.

Deklaracije iji je opseg vidljivosti blok | 649


Deklaracija let i petlja for
Jedini izuzetak koji bih napravio u vezi s eksplicitnim oblikom deklaracije let u bloku je-
ste let deklaracija koja se nalazi u zaglavlju petlje for. Razlog e vam moda izgledati ne-
jasan, ali lino verujem da je to jedna od najvanijih ES6 odlika.
Razmotrite sledee:
var funcs = [];

for (let i = 0; i < 5; i++) {


funcs.push( function(){
console.log( i );
} );
}

funcs[3](); // 3
Izraz let i u zaglavlju petlje for deklarie promenljivu i ne samo za petlju for, nego
ponovo deklarie novu promenljivu i u svakoj iteraciji petlje. To znai da ograde koje se
formiraju unutar jedne iteracije petlje ograuju te promenljive po iteraciji, kao to biste
i oekivali.
Ako isprobate isti primer, ali s deklaracijom var i u zaglavlju petlje for, rezultat e biti
5 umesto 3, zato to bi u spoljanjem opsegu koji funkcija ograuje postojala samo jed-
na promenljiva i, umesto nove promenljive i u svakoj iteraciji koju bi funkcija ogradila.
Isto moete postii i u neznatno opirnijem obliku:
var funcs = [];

for (var i = 0; i < 5; i++) {


let j = i;
funcs.push( function(){
console.log( j );
} );
}

funcs[3](); // 3
U ovom sluaju, izriito deklariemo novu promenljivu j u svakoj deklaraciji, a nakon
toga ograda deluje na isti nain. Drae mi je prvo reenje; zbog dodatne mogunosti koju
ono prua radije se opredeljujem za oblik for (let .. ) ... Moe se opravdano zame-
riti da je to malo implicitniji oblik, ali je za moj ukus ipak dovoljno eksplicitan i koristan.
Deklaracija pomou let ponaa se na isti nain i u petljama for..in i for..of (videti
odeljak Petlja for..of na strani 693).

Deklarisanje konstanti
Postoji jo jedna deklaracija iji opseg vaenja je blok: to je deklaracija pomou rezervi-
sane rei const, ime se formiraju konstante.

650|Poglavlje 27Sintaksa
ta je tano konstanta? To je promenljiva ija se vrednost nakon inicijalizovanja moe
samo itati. Razmotrite sledee:
{
const a = 2;
console.log( a ); // 2

a = 3; // greka TypeError!
}
Nakon inicijalizovanja vrednosti promenljive, u trenutku deklarisanja, vie nije dozvo-
ljeno menjati tu vrednost. Deklaracija const mora sadrati i eksplicitnu operaciju inicija-
lizovanja vrednosti promenljive. Ako elite da vrednost konstante bude undefined, mora-
te deklarisati const a = undefined.
Konstante ni po emu ne ograniavaju samu vrednost, nego samo dodeljivanje vred-
nosti konstanti. Drugim reima, sama vrednost nije zbog deklaracije const postala ni za-
mrznuta, ni nepromenljiva, nego to vai samo za operaciju dodeljivanja vrednosti. Ako
je to neka sloena vrednost, kao to je objekat ili niz, sadraj te vrednosti se i dalje moe
menjati:
{
const a = [1,2,3];
a.push( 4 );
console.log( a ); // [1,2,3,4]

a = 42; // greka TypeError!


}
Promenljiva a ne sadri zaista konstantan niz, nego konstantnu referencu na niz. Sam
niz se slobodno moe menjati.

Dodeljivanje objekta ili niza kao vrednost konstante znai da sakuplja smea
nee moi da pokupi tu vrednost sve dok leksiki opseg vidljivosti te konstante
ne nestane, zato to se nikad ne moe izbrisati referenca na tu vrednost. To moe
biti poeljno, ali vodite rauna ako vam to nije bila namera!

U sutini, deklaracija pomou const sada samo namee ono to smo godinama ugra-
ivali u kd kao stilski oblik, gde smo ime promenljive deklarisali velikim slovima i do-
deljivali joj neku literalnu vrednost za koju smo vodili rauna da je nakon toga nikad ne
menjamo. Nema ogranienja za promenljive deklarisane pomou var, ali ima za promen-
ljive deklarisane pomou const, to moe da vam pomogne da otkrijete nenamerne iz-
mene vrednosti.
Rezervisana re const moe se upotrebiti za deklarisanje promenljivih u petljama for,
for..in i for..of (videti odeljak Petlja for..of na strani 693). Meutim, svaki pokuaj
dodeljivanja nove vrednosti, kao to je u tipinoj odredbi i++ petlje for, prouzrokovae
greku.

Deklaracije iji je opseg vidljivosti blok | 651


Da li koristiti const ili ne
Postoje miljenja da bi u nekim sluajevima JS maina mogla da optimizuje const dekla-
raciju bolje od let ili var deklaracije. Teorijski, tada maina jezika lake prepoznaje da
se vrednost/tip promenljive nee nikad menjati, pa zato moe da eliminie mogu reij-
ski deo posla.
Bez obzira na to da li je const deklaracija zaista korisna ili su to samo nae fantazije i
intuicije, vanija odluka koju treba doneti je da li namerno elite ponaanje konstante ili
ne. Ne zaboravite: jedna od najvanijih uloga vaeg izvornog koda jeste da jasno pokazu-
je ta je tano vaa namera, i to ne samo vama u ovom trenutku, nego i vama ubudue,
kao i drugima koji sarauju s vama na tom kodu.
Neki programeri radije prvo deklariu svaku promenljivu kao const a zatim ublaava-
ju deklaraciju u let ako se u kodu pojavi potreba da se njena vrednost menja. To je zani-
mljiva ideja, ali nije jasno da li to zaista poboljava itljivost ili razumljivost koda.
To nije nekakva zatita, kao to mnogi veruju, zato to svaki programer koji naknad-
no poeli da promeni vrednost promenljive deklarisane kao const moe jednostavno u
njenoj deklaraciji const izmeniti u let. U najboljem sluaju, to titi od sluajnih izmena.
Ali ponovo, izvan naih intuicija i gledita, izgleda da ne postoji nikakva objektivna i ja-
sna mera ta je tano sluajno i kako to preduprediti. Slina oprena miljenja postoje
i u vezi s konverzijom tipova.
Moj savet: da biste izbegli potencijalno zbunjujui kd, koristite const iskljuivo za
deklarisanje promenljivih za koje namerno i oigledno elite da istaknete da se njihove
vrednosti nee menjati. Drugim reima, ponaanje koda nemojte zasnivati na const, nego
tu vrstu deklaracije koristite kao alatku da biste signalizirali nameru, kada se ta namera
moe jasno pokazati.

Funkcije iji je opseg vidljivosti blok


Poev od ES6, opseg vidljivosti funkcija ije se deklaracije nalaze unutar blokova ograni-
en je na blok u kojem su deklarisane. Pre ES6, to nije bilo izriito zadato u specifikaciji,
ali su mnoge implementacije tako radile, pa je sada specifikacija usklaena sa stvarnou.
Razmotrite sledee:
{
foo(); // radi!

function foo() {
// ..
}
}

foo(); // ReferenceError
Funkcija foo() je deklarisana unutar bloka { .. } i za ES6 njen opseg vidljivosti je taj
blok. Dakle, ona nije vidljiva izvan tog bloka. Ali obratite panju i na to da je deklaracija
funkcije podignuta na poetak bloka, za razliku od let deklaracija, koje uvode ve ra-
nije pomenutu zamku TDZ greke.

652|Poglavlje 27Sintaksa
Deklarisanje funkcija iji je opseg vidljivosti blok moe biti problem ako ste navi-
kli da piete kd kao to je sledei i oekujete ponaanje kao ranije, kad opseg vidljivo-
sti nije bio blok:
if (something) {
function foo() {
console.log( 1 );
}
}
else {
function foo() {
console.log( 2 );
}
}

foo(); // ??
U okruenjima starijim od ES6, funkcija foo() bi uvek ispisala 2 bez obzira na vred-
nost something, zato to su se obe deklaracije funkcije izvlaile iz svojih blokova i uvek bi
vaila samo poslednja deklaracija.
U ES6, poslednji red izaziva greku ReferenceError.

Operator za razdvajanje/grupisanje ostatka vrednosti


ES6 uvodi nov operator ... koji se obino naziva operator za razdvajanje ili operator za
grupisanje ostatka vrednosti (engl. spread ili rest operator), zavisno od toga gde/kako je
upotrebljen. Pogledajmo kako to izgleda:
function foo(x,y,z) {
console.log( x, y, z );
}

foo( ... [1,2,3] ); // 1 2 3


Kada operator ... zadate ispred nekog niza (zapravo, ispred svakog iterabilnog objek-
ta, to emo razmotriti u poglavlju 28), on se ponaa tako da razdvaja niz na njegove
pojedinane elemente.
Na sluajeve upotrebe u obliku kao to je prikazan u primeru, nailaziete kada treba
razdvojiti zadati niz na skup argumenata u pozivu funkcije. Operator ... se u tom slu-
aju ponaa kao jednostavnija sintaksna zamena metode apply(..), koju bismo pre ES6
najee upotrebili u sledeem obliku:
foo.apply( null, [1,2,3] ); // 1 2 3
Ali, operator ... moe se iskoristiti za razdvajanje/izdvajanje vrednosti i u drugim
kontekstima recimo, unutar deklaracije niza:
var a = [2,3,4];
var b = [ 1, ...a, 5 ];

console.log( b ); // [1,2,3,4,5]

Operator za razdvajanje/grupisanje ostatka vrednosti | 653

You might also like