JavaScript Closures

A closure is a function that has access to variables from its outer (enclosing) function's scope, even after the outer function has returned. Closures are one of the most important and powerful concepts in JavaScript.

What is a Closure?

A closure is created when a function is defined inside another function. The inner function 'closes over' the variables of the outer function, retaining access to them even after the outer function finishes executing.

Basic Closure
function outer() {
  const message = 'Hello from outer!';

  function inner() {
    // inner has access to 'message' from outer's scope
    console.log(message);
  }

  return inner;
}

const myFunc = outer();
// outer() has finished, but inner still has access to 'message'
myFunc();
myFunc();

Lexical Scope

Closures rely on lexical scoping, which means a function's scope is determined by where it is defined in the source code, not where it is called. Inner functions can access variables from all outer scopes.

Lexical Scope Chain
function grandparent() {
  const a = 'grandparent';

  function parent() {
    const b = 'parent';

    function child() {
      const c = 'child';
      // Has access to all outer scopes
      console.log(a + ' > ' + b + ' > ' + c);
    }

    child();
  }

  parent();
}

grandparent();

Function Returning Function

The most common closure pattern is a function that returns another function. The returned function retains access to the variables of the enclosing function.

Function Factory
function multiplier(factor) {
  // 'factor' is closed over by the returned function
  return function(number) {
    return number * factor;
  };
}

const double = multiplier(2);
const triple = multiplier(3);
const times10 = multiplier(10);

console.log('double(5): ' + double(5));
console.log('triple(5): ' + triple(5));
console.log('times10(5): ' + times10(5));
console.log('double(7): ' + double(7));

Private Variables

Closures can create private variables that are not accessible from outside the function. This is a powerful pattern for data encapsulation and information hiding.

Private Variables with Closures
function createBankAccount(initialBalance) {
  let balance = initialBalance;  // Private variable

  return {
    deposit: function(amount) {
      balance += amount;
      console.log('Deposited: $' + amount);
    },
    withdraw: function(amount) {
      if (amount > balance) {
        console.log('Insufficient funds!');
        return;
      }
      balance -= amount;
      console.log('Withdrawn: $' + amount);
    },
    getBalance: function() {
      return balance;
    }
  };
}

const account = createBankAccount(100);
account.deposit(50);
account.withdraw(30);
console.log('Balance: $' + account.getBalance());
// balance is not accessible directly
console.log('Direct access: ' + typeof account.balance);

Counter Example

A classic closure example is a counter function. Each call to the factory creates an independent counter with its own private state.

Counter with Closure
function createCounter(start) {
  let count = start || 0;

  return {
    increment: function() { return ++count; },
    decrement: function() { return --count; },
    getCount: function() { return count; },
    reset: function() { count = start || 0; return count; }
  };
}

const counter1 = createCounter(0);
const counter2 = createCounter(10);

console.log('C1: ' + counter1.increment());
console.log('C1: ' + counter1.increment());
console.log('C1: ' + counter1.increment());
console.log('C2: ' + counter2.increment());
console.log('C2: ' + counter2.decrement());
console.log('C1 count: ' + counter1.getCount());
console.log('C2 count: ' + counter2.getCount());
// Each counter is independent!

Practical Uses and Memory Considerations

Closures are used extensively in JavaScript for event handlers, callbacks, module patterns, and partial application. However, closures keep their outer variables in memory, which can lead to memory leaks if not managed carefully.

Closures in Event Handlers and Callbacks
// Closure in setTimeout
function delayedGreeting(name) {
  setTimeout(function() {
    console.log('Hello, ' + name + '!');
  }, 100);
}

delayedGreeting('Alice');
delayedGreeting('Bob');

// Closure for partial application
function logger(prefix) {
  return function(message) {
    console.log('[' + prefix + '] ' + message);
  };
}

const infoLog = logger('INFO');
const errorLog = logger('ERROR');

infoLog('Application started');
errorLog('Something went wrong');
infoLog('Request processed');
Use CaseDescription
Data privacyCreate private variables inaccessible from outside
Function factoriesCreate functions with pre-configured behavior
Event handlersMaintain state in callback functions
Module patternEncapsulate related functionality
Partial applicationPre-fill some arguments of a function
MemoizationCache computed results
📝 Note: Closures keep references to outer variables, not copies. If the outer variable changes, the closure sees the updated value. This is why using var in a for loop with setTimeout gives unexpected results - use let (which creates a new binding per iteration) to fix this.
Exercise:
What is a closure in JavaScript?
Try it YourselfCtrl+Enter to run
Click Run to see the output here.