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!"); };