Scopes, Scope Chains, and Closures
Understanding scope and closures is key to mastering advanced JavaScript. They control where variables can be accessed and how memory is managed during execution.
1. What is Scope?
Scope determines the visibility or accessibility of variables in your code. JavaScript has three types of scope:
- Global Scope: Variables declared outside any function or block are globally accessible.
- Function Scope: Variables declared inside a function (using
var,let, orconst) are only accessible within that function. - Block Scope: Variables declared with
letorconstinside a{}block are only accessible within that block.
2. Lexical Scope and the Scope Chain
JavaScript uses Lexical Scope, meaning the accessibility of variables is determined by the physical position of code at write-time (where functions are defined, not where they are called).
Scope Chain:
When a variable is used, the JavaScript engine tries to find it in the current local scope. If it cannot find it, it searches the parent outer scope. This lookup continues up the hierarchy until it reaches the Global Scope. This sequence of nesting is called the Scope Chain.
let globalVal = "global";
function outer() {
let outerVal = "outer";
function inner() {
let innerVal = "inner";
console.log(innerVal); // "inner" (found locally)
console.log(outerVal); // "outer" (found in parent scope)
console.log(globalVal); // "global" (found in global scope)
}
inner();
}
outer();3. What is a Closure?
A closure is created when an inner function is defined inside an outer function, and the inner function refers to variables from the outer function. Even after the outer function finishes executing, the inner function remembers and retains access to those outer variables.
function makeCounter() {
let count = 0; // Local variable
return function() {
count++; // Accesses parent scope variable
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2How does this work?
Normally, when a function completes execution, its variables are cleaned up by garbage collection. However, since the returned inner function still references count, JavaScript keeps count in memory. This is a closure.
4. Practical Use Cases of Closures
A. Data Privacy (Private Variables)
You can use closures to encapsulate data and prevent external code from modifying it directly:
function createBankAccount(initialBalance) {
let balance = initialBalance; // Private state
return {
deposit: (amount) => { balance += amount; },
withdraw: (amount) => {
if (amount <= balance) {
balance -= amount;
return true;
}
return false;
},
getBalance: () => balance
};
}
const account = createBankAccount(100);
account.deposit(50);
console.log(account.getBalance()); // 150
console.log(account.balance); // undefined (cannot access private variable directly!)B. Function Factories
Creating functions with pre-configured parameters:
function makeMultiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
console.log(double(10)); // 20
console.log(triple(10)); // 30In the next module, we will explore objects and advanced array methods in JavaScript.