JavaScript Scope & Closures
Scope determines where variables are accessible. Closures let functions remember their creation environment.
What Is Scope?
let globalVar = "I am global";
function example() {
let localVar = "I am local";
console.log(globalVar); // accessible
console.log(localVar); // accessible
}
example();
// console.log(localVar); // Error
Remember: Variables are only accessible within their scope.
Function vs Block Scope
// Function scope — var leaks out
for (var j = 0; j < 3; j++) {}
console.log(j); // 3
// Block scope — let stays inside
for (let i = 0; i < 3; i++) {}
console.log(i); // Error: i is not defined
Tip: Always use let and const instead of var.
Closures
A closure is a function that remembers its outer scope:
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
let counter = createCounter();
counter(); // 1
counter(); // 2
counter(); // 3
Remember: The inner function retains access to count even after createCounter() returns.
Practical Closures: Data Privacy
function createUser(name) {
let _name = name;
return {
getName() { return _name; },
setName(newName) { _name = newName; }
};
}
let user = createUser("John");
user.getName(); // "John"
user.setName("Jane");
user.getName(); // "Jane"
Complete Example: Memoization
function memoize(fn) {
let cache = {};
return function(...args) {
let key = JSON.stringify(args);
if (key in cache) return cache[key];
let result = fn(...args);
cache[key] = result;
return result;
};
}
let square = memoize((n) => n * n);
square(5); // 25 (computed)
square(5); // 25 (cached)
Best Practices
- Use
letandconst— for block-scoped variables - Minimize global scope — wrap code in modules
- Use closures for privacy — hide internal state
- Avoid closures in loops — can cause unexpected behavior
Common Mistakes
- Using
varin loops — leads to scoping bugs - Closures in loops — all closures reference the same variable
- Memory leaks — closures holding references to large objects
- Overusing globals — causes naming conflicts