JavaScript Function Closures
Understanding how functions remember their environment
🔐 What are Closures?
A closure is a function that has access to variables from its outer (enclosing) scope even after the outer function has finished executing. It's like the function "remembers" its environment.
// Simple closure example
function outerFunction(x) {
// This is the outer function's scope
function innerFunction(y) {
// Inner function has access to 'x'
return x + y;
}
return innerFunction;
}
const addFive = outerFunction(5);
console.log(addFive(3)); // 8 (5 + 3)
Output:
8
Key Closure Concepts
Memory
Functions remember their environment
function outer() {
let x = 10;
return function() { return x; };
}
Private Variables
Create private data encapsulation
function counter() {
let count = 0;
return () => ++count;
}
Function Factory
Create specialized functions
function multiplier(factor) {
return x => x * factor;
}
Module Pattern
Create modules with public/private methods
const module = (function() {
let private = 0;
return { get: () => private };
})();
🔹 Basic Closure Example
Here's how closures work step by step:
function createGreeting(name) {
// This variable is in the outer function's scope
const greeting = `Hello, ${name}!`;
// Inner function has access to 'greeting'
function sayHello() {
return greeting;
}
return sayHello;
}
// Create a closure
const greetJohn = createGreeting('John');
const greetMary = createGreeting('Mary');
// Even though createGreeting finished, the inner function remembers 'greeting'
console.log(greetJohn()); // "Hello, John!"
console.log(greetMary()); // "Hello, Mary!"
Output:
Hello, John!
Hello, Mary!
🔹 Counter with Closures
Closures are perfect for creating private variables:
function createCounter() {
let count = 0; // Private variable
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getCount: function() {
return count;
}
};
}
const counter1 = createCounter();
const counter2 = createCounter();
console.log(counter1.increment()); // 1
console.log(counter1.increment()); // 2
console.log(counter2.increment()); // 1 (separate counter)
console.log(counter1.getCount()); // 2
Output:
1
2
1
2
🔹 Function Factory Pattern
Use closures to create specialized functions:
// Function factory for mathematical operations
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
// Create specialized functions
const double = createMultiplier(2);
const triple = createMultiplier(3);
const quadruple = createMultiplier(4);
console.log(double(5)); // 10
console.log(triple(4)); // 12
console.log(quadruple(3)); // 12
// Function factory for validators
function createValidator(minLength) {
return function(input) {
return input.length >= minLength;
};
}
const isValidPassword = createValidator(8);
const isValidUsername = createValidator(3);
console.log(isValidPassword('12345')); // false
console.log(isValidPassword('12345678')); // true
console.log(isValidUsername('jo')); // false
console.log(isValidUsername('john')); // true
Output:
10
12
12
false
true
false
true
🔹 Module Pattern with Closures
Create modules with public and private methods:
const bankAccount = (function() {
// Private variables
let balance = 0;
let accountNumber = '12345';
// Private function
function validateAmount(amount) {
return amount > 0 && typeof amount === 'number';
}
// Public API
return {
deposit: function(amount) {
if (validateAmount(amount)) {
balance += amount;
return `Deposited $${amount}. New balance: $${balance}`;
}
return 'Invalid amount';
},
withdraw: function(amount) {
if (validateAmount(amount) && amount <= balance) {
balance -= amount;
return `Withdrew $${amount}. New balance: $${balance}`;
}
return 'Invalid amount or insufficient funds';
},
getBalance: function() {
return `Current balance: $${balance}`;
},
getAccountNumber: function() {
return `Account: ${accountNumber}`;
}
};
})();
console.log(bankAccount.deposit(100)); // Deposited $100. New balance: $100
console.log(bankAccount.withdraw(30)); // Withdrew $30. New balance: $70
console.log(bankAccount.getBalance()); // Current balance: $70
// console.log(balance); // Error: balance is not accessible
Output:
Deposited $100. New balance: $100
Withdrew $30. New balance: $70
Current balance: $70