Vaikka JavaScript-käyttääkin aaltosulkeita blokkien ilmaisuun, se ei tue blokkinäkyvyyttä. Tämä tarkoittaa sitä, että kieli tukee ainoastaan *funktionäkyvyyttä.
function test() { // näkyvyysalue
for(var i = 0; i < 10; i++) { // tämä ei ole näkyvyysalue
// count
}
console.log(i); // 10
}
Huomio: Mikäli
return-lausetta ei käytetä sijoitukseen,{...}-notaatio tulkitaan blokkina eikä olioliteraalina. Tästä ja [puolipisteiden automaattisesta lisäämisestä](#core.semicolon] seuraa yllättäviä virheitä.
JavaScript ei myöskään sisällä erityistä tukea nimiavaruuksille. Tämä tarkoittaa sitä, että kaikki määritellään oletuksena globaalissa nimiavaruudessa.
Joka kerta kun muuttujaan viitataan, JavaScript käy kaikki näkyvyysalueet läpi alhaalta lähtien. Mikäli se saavuttaa globaalin näkyvyystalueen, eikä löydä haettua nimeä, se palauttaa ReferenceError-virheen.
// skripti A
foo = '42';
// skripti B
var foo = '42'
Yllä olevat skriptit käyttäytyvät eri tavoin. Skripti A määrittelee muuttujan nimeltä foo globaalissa näkyvyysalueessa. Skripti B määrittelee foo-muuttujan vallitsevassa näkyvyysalueessa.
Tämä ei ole sama asia. var-avainsanan käyttämättä jättäminen voi johtaa vakaviin seurauksiin.
// globaali näkyvyysalue
var foo = 42;
function test() {
// paikallinen näkyvyysalue
foo = 21;
}
test();
foo; // 21
var-avainsanan pois jättäminen johtaa siihen, että funktio test ylikirjoittaa foo:n arvon. Vaikka tämä ei välttämättä vaikutakaan suurelta asialta, tuhansien rivien tapauksessa var-avainsanan käyttämättömyys voi johtaa vaikeasti löydettäviin bugeihin.
// globaali näkyvyysalue
var items = [/* joku lista */];
for(var i = 0; i < 10; i++) {
subLoop();
}
function subLoop() {
// aliluupin näkyvyysalue
for(i = 0; i < 10; i++) { // hups, var jäi pois
// jotain makeaa ja hienoa
}
}
Tässä tapauksessa ulomman luupin suoritus lopetetaan ensimmäisen subLoop-kutsun jälkeen. Tämä johtuu siitä, että se ylikirjoittaa i:n globaalin arvon. Mikäli jälkimmäisessä luupissa olisi käytetty var-avainsanaa, olisi ikävyyksiltä vältytty. var-avainsanaa ei siis tule ikinä jättää pois ellei siihen ole hyvää syytä.
Ainoastaan funktion parametrit ja muuttujat, jotka sisältävät var-määreen ovat paikallisia.
// globaali näkyvyysalue
var foo = 1;
var bar = 2;
var i = 2;
function test(i) {
// paikallinen näkyvyysalue
i = 5;
var foo = 3;
bar = 4;
}
test(10);
foo ja i ovatkin test-funktiolle paikallisia. bar sijoitus muuttaa globaalin muuttujan arvoa.
JavaScript hilaa määreitä. Tämä tarkoittaa sitä, että sekä var-lausekkeet että function-määreet siirretään ne sisältävän näkyvyysalueen huipulle.
bar();
var bar = function() {};
var someValue = 42;
test();
function test(data) {
if (false) {
goo = 1;
} else {
var goo = 2;
}
for(var i = 0; i < 100; i++) {
var e = data[i];
}
}
Yllä olevaa koodia muutetaan ennen suoritusta. JavaScript siirtää var-lausekkeet ja function-määreet lähimmän näkyvyysalueen huipulle.
// var-lausekkeet siirrettiin tänne
var bar, someValue; // oletuksena 'undefined'
// myös funktio-määre siirtyi tänne
function test(data) {
var goo, i, e; // ei blokkinäkyvyyttä, siirretään siis tänne
if (false) {
goo = 1;
} else {
goo = 2;
}
for(i = 0; i < 100; i++) {
e = data[i];
}
}
bar(); // TypeError-virhe, baria ei ole vielä määritelty
someValue = 42; // hilaus ei koske sijoituksia
bar = function() {};
test();
Sen lisäksi, että puuttuva blokkinäkyvyys siirtää var-lausekkeet luuppien ulkopuolelle, tekee se myös eräistä if-rakenteista vaikeita käsittää.
Alkuperäisessä koodissa if-lause näytti muokkaavan globaalia muuttujaa goo. Todellisuudessa se muokkaa paikallista muuttujaa varsinaisen hilauksen jälkeen.
Seuraava koodi saattaisi ensi näkemältä aiheuttaa ReferenceError-virheen. Näin ei kuitenkaan tapahdu hilauksen ansiosta.
// onko SomeImportantThing alustettu
if (!SomeImportantThing) {
var SomeImportantThing = {};
}
Tämä toimii, koska var-lauseke on hilattu globaalin näkyvyysalueen huipulle.
var SomeImportantThing;
// mahdollista alustuskoodia
// onhan se alustettu
if (!SomeImportantThing) {
SomeImportantThing = {};
}
Kaikki JavaScriptin näkyvyysalueet, globaalin näkyvyysalue mukaanlukien, sisältävät erikoismuuttujan this. this viittaa tämänhetkiseen olioon.
Funktioiden näkyvyysalueet sisältävät myös arguments-olion. Se sisältää funktiolle annetut argumentit.
Mikäli näkyvyysalueen sisällä pyritään pääsemään käsiksi esimerkiksi foo:n arvoon JavaScript käyttäytyy seuraavasti:
- Mikäli
var foo-lauseke löytyy tämänhetkisestä näkyvyysalueesta, käytä sen arvoa. - Mikäli eräs funktion parametreista on
foo, käytä sitä. - Mikäli funktion nimi itsessään on
foo, käytä sitä. - Siirry ulompaan näkyvyysalueeseen ja suorita #1 uudelleen.
Huomio: Mikäli funktio sisältää
arguments-nimisen parametrin, estää searguments-olion luonnin kokonaan.
Globaalin nimiavaruuden ongelmana voidaan pitää nimitörmäyksiä. JavaScriptissä tätä ongelmaa voidaan kiertää käyttämällä nimettömiä kääreitä.
(function() {
// "nimiavaruus" itsessään
window.foo = function() {
// paljastettu sulkeuma
};
})(); // suorita funktio heti
Nimettömiä funktioita pidetään lauseina. Jotta niitä voidaan kutsua, tulee ne suorittaa ensin.
( // suorita sulkeiden sisältämä funktio
function() {}
) // ja palauta funktio-olio
() // kutsu suorituksen tulosta
Samaan lopputulokseen voidaan päästä myös hieman eri syntaksia käyttäen.
// Kaksi muuta tapaa
+function(){}();
(function(){}());
On suositeltavaa käyttää nimettömiä kääreitä nimiavaruuksina. Sen lisäksi, että se suojelee koodia nimitörmäyksiltä, se tarjoaa keinon jaotella ohjelma paremmin.
Globaalien muuttujien käyttöä pidetään yleisesti huonona tapana. Mikä tahansa niiden käyttö viittaa huonosti kirjoitettuun, virheille alttiiseen ja hankalasti ylläpidettävään koodiin.