Az olyan alapfogalmak, mint a scope (hatókör), a closure (lezárás) és a hoisting (emelés), kulcsfontosságúak ahhoz, hogy hatékonyan és hibamentesen dolgozhassunk. Ezek a koncepciók meghatározzák, hogy a változók és függvények hol és hogyan érhetők el, hogyan viselkednek a különböző hatókörökben, és miként kezelik a JavaScript belső mechanizmusai a kódunkat. Ebben a cikkben röviden bemutatjuk ezeket a fogalmakat, hogy még tisztább képet kapj róluk.
Scope(hatókör)
A scope (hatókör) JavaScriptben azt határozza meg, hogy a változók, függvények és objektumok hol érhetők el a kódban, és hol használhatók. A scope segít elkerülni a változók közötti konfliktusokat és szabályozza az adatokhoz való hozzáférést.
1. Globális hatókör (Global Scope)
Azok a változók, amelyek a kód legfelső szintjén vannak definiálva (nem egy függvény vagy blokk belsejében), globális hatókörrel rendelkeznek. Ezek a változók bárhonnan elérhetők a programban:
let globalVar = "Ez globális változó";
function printGlobalVar() {
console.log(globalVar); // Hozzáfér a globális változóhoz
}
printGlobalVar(); // "Ez globális változó"
2. Függvény hatókör (Function Scope)
Egy függvényen belül definiált változók csak azon belül érhetők el, ahol deklarálták őket. A függvényen kívül ezek a változók nem hozzáférhetők:
function myFunction() {
let localVar = "Ez lokális változó"; // Lokális változó
console.log(localVar);
}
myFunction(); // "Ez lokális változó"
console.log(localVar); // Hiba: localVar is not defined
3. Blokk hatókör (Block Scope)
A let és const kulcsszavakkal deklarált változók blokkszintű hatókörrel rendelkeznek. Egy blokk a {} közé zárt kódrészlet. A blokk hatókörű változók csak azon a blokkon belül érhetők el, ahol deklarálták őket:
{
let blockScoped = "Blokk hatókörű változó";
console.log(blockScoped); // "Blokk hatókörű változó"
}
console.log(blockScoped); // Hiba: blockScoped is not defined
4. Lexikális hatókör (Lexical Scope)
A belső függvények hozzáférhetnek a külső függvény hatóköréhez, de fordítva nem. A JavaScript statikus vagy lexikális hatókörű nyelv, tehát a változók elérhetősége a kód írásának helyétől függ, nem a futás közbeni környezettől:
function outerFunction() {
let outerVar = "Külső változó";
function innerFunction() {
console.log(outerVar); // Hozzáfér a külső változóhoz
}
innerFunction();
}
outerFunction(); // "Külső változó"
Példa a különböző hatókörök kombinációjára
let globalVar = "Globális";
function outerFunction() {
let outerVar = "Függvény hatókör";
{
let blockVar = "Blokk hatókör";
console.log(globalVar); // Globális
console.log(outerVar); // Függvény hatókör
console.log(blockVar); // Blokk hatókör
}
// console.log(blockVar); // Hiba: blockVar is not defined
}
outerFunction();
Closure(lezárás)
A closure (lezárás) a JavaScript egyik erőteljes koncepciója, amely akkor jön létre, amikor egy belső függvény hozzáfér a külső függvény változóihoz, még akkor is, ha a külső függvény már végrehajtódott. Ez azért lehetséges, mert a belső függvény megőrzi a környezetét, amelyben létrejött.
A belső függvény „lezárja” a külső függvény hatókörét, így hozzáférést nyer annak változóihoz, még a külső függvény lefutása után is:
function makeCounter() {
let count = 0; // Ez a változó a closure része lesz
return function () {
count++; // Hozzáfér a külső függvény változójához
return count;
};
}
const counter1 = makeCounter(); // Létrehoz egy új closure-t
console.log(counter1()); // 1
console.log(counter1()); // 2
console.log(counter1()); // 3
const counter2 = makeCounter(); // Egy másik closure-t hozunk létre
console.log(counter2()); // 1 (mivel külön hatókörben van)
console.log(counter1()); // 4 (az első closure saját állapotát tartja fenn)
Hoisting(emelés)
A hoisting (emelés) egy JavaScript viselkedés, amelyben a változók és függvénydeklarációk „felkerülnek” a hatókörük tetejére a kód végrehajtása előtt. Ez azt jelenti, hogy a változókat és függvényeket már azelőtt elérhetjük, hogy a kódban ténylegesen definiálnánk őket.
1. Változók hoistingja
A változók deklarációja (csak a deklaráció!) felkerül a hatókör tetejére. Az értékadás viszont a helyén marad. Ha egy var-ral deklarált változót használunk a deklaráció előtt, az értéke undefined lesz:
console.log(a); // undefined (hoisting miatt létezik, de még nem kapott értéket) var a = 5; console.log(a); // 5
2. let és const hoistingja
A let és const változók szintén hoisting alá esnek, de az ún. „Temporal Dead Zone” (TDZ) miatt nem érhetők el a deklarációjuk előtt:
console.log(b); // Hiba: Cannot access 'b' before initialization let b = 10; const c = 20; console.log(c); // 20
3. Függvények hoistingja
A függvénydeklarációk teljes egészében „felkerülnek” a hatókör tetejére, így a függvényt a deklaráció előtt is meghívhatjuk:
sayHello(); // "Hello, world!" (hoisting miatt működik)
function sayHello() {
console.log("Hello, world!");
}
Viszont a függvénykifejezések (pl. var, let vagy const változókhoz rendelt függvények) másképp viselkednek, mert ezeknél csak a változó deklarációja kerül „fel”, de maga a függvénykifejezés nem:
sayHi(); // Hiba: sayHi is not a function
var sayHi = function() {
console.log("Hi!");
};