// Basic closure example
function outer() {
const message = 'Hello from outer';
function inner() {
console.log(message); // Accesses outer variable
}
return inner;
}
const closure = outer();
closure(); // "Hello from outer"
// Counter with closure (data privacy)
function createCounter() {
let count = 0; // Private variable
return {
increment() {
count++;
return count;
},
decrement() {
count--;
return count;
},
getCount() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount()); // 2
// console.log(counter.count); // undefined - private!
// Multiple independent closures
const counter1 = createCounter();
const counter2 = createCounter();
counter1.increment(); // 1
counter2.increment(); // 1
counter2.increment(); // 2
console.log(counter1.getCount()); // 1 (independent)
// Closure in loops (classic problem)
// Wrong way
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // Prints 3, 3, 3
}, 100);
}
// Fixed with IIFE
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(function() {
console.log(j); // Prints 0, 1, 2
}, 100);
})(i);
}
// Modern fix with let
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // Prints 0, 1, 2
}, 100);
}
// Function factory with closures
function multiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = multiplier(2);
const triple = multiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// Private methods pattern
function BankAccount(initialBalance) {
let balance = initialBalance; // Private
// Private method
function validateAmount(amount) {
return typeof amount === 'number' && amount > 0;
}
// Public methods
this.deposit = function(amount) {
if (validateAmount(amount)) {
balance += amount;
return balance;
}
throw new Error('Invalid amount');
};
this.withdraw = function(amount) {
if (validateAmount(amount) && amount <= balance) {
balance -= amount;
return balance;
}
throw new Error('Invalid or insufficient amount');
};
this.getBalance = function() {
return balance;
};
}
const account = new BankAccount(100);
account.deposit(50); // 150
account.withdraw(30); // 120
console.log(account.getBalance()); // 120
// account.balance // undefined - private!
// Closure with event handlers
function setupButton(buttonId, message) {
const button = document.getElementById(buttonId);
button.addEventListener('click', function() {
console.log(message); // Closure over message
});
}
setupButton('btn1', 'Button 1 clicked');
setupButton('btn2', 'Button 2 clicked');
// Module pattern
const calculator = (function() {
// Private variables and functions
let result = 0;
function validateNumber(n) {
if (typeof n !== 'number') {
throw new Error('Not a number');
}
}
// Public API
return {
add(n) {
validateNumber(n);
result += n;
return this;
},
subtract(n) {
validateNumber(n);
result -= n;
return this;
},
multiply(n) {
validateNumber(n);
result *= n;
return this;
},
getResult() {
return result;
},
reset() {
result = 0;
return this;
}
};
})();
calculator.add(5).multiply(3).subtract(2);
console.log(calculator.getResult()); // 13
// Memoization with closure
function memoize(fn) {
const cache = {}; // Closure variable
return function(...args) {
const key = JSON.stringify(args);
if (key in cache) {
console.log('From cache');
return cache[key];
}
console.log('Calculating...');
const result = fn(...args);
cache[key] = result;
return result;
};
}
const expensiveSum = memoize((a, b) => {
// Simulate expensive operation
for (let i = 0; i < 1000000000; i++) {}
return a + b;
});
console.log(expensiveSum(5, 3)); // Calculating... 8
console.log(expensiveSum(5, 3)); // From cache 8
// Partial application
function partial(fn, ...fixedArgs) {
return function(...remainingArgs) {
return fn(...fixedArgs, ...remainingArgs);
};
}
function greet(greeting, name) {
return `${greeting}, ${name}!`;
}
const sayHello = partial(greet, 'Hello');
console.log(sayHello('Alice')); // "Hello, Alice!"
console.log(sayHello('Bob')); // "Hello, Bob!"
// Lexical scope example
const globalVar = 'global';
function outer() {
const outerVar = 'outer';
function middle() {
const middleVar = 'middle';
function inner() {
const innerVar = 'inner';
// Can access all outer scopes
console.log(innerVar); // 'inner'
console.log(middleVar); // 'middle'
console.log(outerVar); // 'outer'
console.log(globalVar); // 'global'
}
inner();
}
middle();
}
outer();