Understanding Variable Scope in JavaScript
Variable scope determines where a name can be accessed. JavaScript uses lexical scope, with important differences between var, let, and const.
Global Scope
A binding declared at the top level can be visible throughout its module or script:
const globalValue = 'global';
function showValue() {
console.log(globalValue);
}In modern ES modules, top-level declarations do not automatically become properties of window.
Function Scope
var is function-scoped:
function example() {
var message = 'inside';
console.log(message);
}
// console.log(message); // ReferenceError
Block Scope
let and const are scoped to their nearest block:
if (true) {
const blockValue = 'inside the block';
console.log(blockValue);
}
// console.log(blockValue); // ReferenceError
This is one reason modern JavaScript generally prefers const and let over var.
Lexical Scope and Closures
Inner functions can access bindings from the scope where they were defined:
function makeCounter() {
let count = 0;
return function increment() {
count += 1;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
The returned function retains access to count; this behavior is called a closure.
Practical Guidelines
- Prefer
constunless a binding must be reassigned. - Use
letfor intentional reassignment. - Keep scope as narrow as practical.
- Avoid unnecessary globals.
- Understand closures before using them for long-lived state or callbacks.
Conclusion
JavaScript scope is lexical: code structure determines which bindings are visible. Knowing how function scope, block scope, and closures interact helps prevent accidental name collisions and state bugs.