JavaScript Scope and Hoisting
Two fundamental concepts that affect how variables behave in JavaScript are scope and hoisting. Understanding them is essential for writing bug-free, predictable code.
What is Scope?
Scope defines where a variable is accessible in code. A variable declared in one part of the code may or may not be accessible in another part — that depends on its scope.
Real-life analogy: Variables are like people with access cards. A global employee can enter any room. A local employee can only enter their own department. Scope defines which "rooms" (code blocks) a variable can enter.
Types of Scope
- Global Scope — Available everywhere in the program
- Function Scope — Available only inside the function
- Block Scope — Available only inside the block
{ }
Global Scope
A variable declared outside of any function or block is in the global scope. It can be accessed from anywhere in the code.
let appName = "eStudy247"; // Global variable
function displayName() {
console.log(appName); // Accessible inside function
}
displayName(); // eStudy247
console.log(appName); // Also accessible hereFunction Scope
Variables declared inside a function are only accessible within that function. They cease to exist once the function finishes executing.
function calculateTax() {
let taxRate = 0.18; // Local to this function
console.log(taxRate); // Works fine here
}
calculateTax(); // 0.18
// console.log(taxRate); // Error! taxRate is not defined hereEach function has its own scope. Variables with the same name in different functions do not conflict.
function functionA() {
let message = "From A";
console.log(message); // From A
}
function functionB() {
let message = "From B"; // Different variable, same name — no conflict
console.log(message); // From B
}
functionA();
functionB();Block Scope (let and const)
With let and const, a variable declared inside any code block { } is only accessible within that block. This is called block scope.
if (true) {
let blockVar = "I am inside the block";
console.log(blockVar); // Works here
}
// console.log(blockVar); // Error! blockVar is not accessible outside the blockfor (let i = 0; i < 3; i++) {
console.log(i); // Works: 0, 1, 2
}
// console.log(i); // Error! i only exists inside the for loopvar Has No Block Scope
This is why var can cause unexpected bugs — it ignores block boundaries.
if (true) {
var leaked = "I escaped the block!";
}
console.log(leaked); // Works! (var leaks out of the block)
if (true) {
let safe = "I stay inside.";
}
// console.log(safe); // Error! (let respects the block)Scope Chain
When JavaScript looks for a variable, it starts in the current scope and moves outward (to parent scopes) until it either finds the variable or reaches the global scope. This process is called the scope chain.
let level = "global";
function outer() {
let level = "outer function";
function inner() {
// No local "level" here — JavaScript looks in parent scopes
console.log(level); // "outer function" (found in outer scope)
}
inner();
}
outer();Lexical Scope
JavaScript uses lexical (static) scoping — a function's scope is determined by where it is defined, not where it is called.
let country = "India";
function showCountry() {
console.log(country); // Uses the "country" from where the function was defined
}
function changeContext() {
let country = "Nepal";
showCountry(); // Still prints "India" — not "Nepal"
}
changeContext(); // IndiaWhat is Hoisting?
Hoisting is JavaScript's default behavior of moving declarations to the top of their scope before the code executes. This means some variables and functions can be used before they appear in the source code.
Function Hoisting
Function declarations are fully hoisted — they can be called before they are defined.
greet(); // Works! Even though function is defined below
function greet() {
console.log("Hello from hoisted function!");
}
// Output: Hello from hoisted function!var Hoisting
Variables declared with var are hoisted to the top, but only the declaration — not the assigned value. The variable exists but holds undefined until the assignment line is reached.
console.log(message); // undefined (not an error, but not the value either)
var message = "Hello";
console.log(message); // HelloThis is what JavaScript actually does internally with var:
var message; // Declaration hoisted to the top
console.log(message); // undefined
message = "Hello"; // Assignment stays in place
console.log(message); // Hellolet and const Hoisting — Temporal Dead Zone
let and const are also hoisted, but they are NOT initialized. Accessing them before their declaration line causes a ReferenceError. The period between the start of the scope and the variable's declaration is called the Temporal Dead Zone (TDZ).
// console.log(name); // ReferenceError: Cannot access 'name' before initialization
let name = "Pooja";
console.log(name); // PoojaFunction Expression and Arrow Function Hoisting
Function expressions and arrow functions are NOT hoisted. They behave like regular variables.
// greet(); // Error! Cannot access before initialization
const greet = () => {
console.log("Hello!");
};
greet(); // Works after declarationHoisting Summary Table
| Declaration Type | Hoisted? | Initial Value Before Declaration |
|---|---|---|
| var | Yes | undefined |
| let | Yes (TDZ) | ReferenceError |
| const | Yes (TDZ) | ReferenceError |
| function declaration | Yes (fully) | Full function available |
| function expression / arrow | No | ReferenceError |
Practical Example — Scope in Action
let total = 0; // Global
function addToCart(price) {
let discount = 10; // Local to addToCart
total += price - discount; // Accesses global "total"
}
addToCart(100); // total = 90
addToCart(200); // total = 90 + 190 = 280
console.log(total); // 280
// console.log(discount); // Error — not accessible outside functionKey Points to Remember
- Scope controls where variables are accessible in code
- Global variables are accessible everywhere; local variables are limited to their function or block
letandconstare block-scoped;varis function-scoped- Avoid
var— block-scope leaking causes hard-to-find bugs - Hoisting moves declarations to the top of their scope before execution
varis hoisted with valueundefined; accessing before assignment returns undefinedletandconstare in the Temporal Dead Zone until their declaration — accessing them before causes a ReferenceError- Function declarations are fully hoisted — they can be called before they are written
