What will be the output? (Closures)

Medium•

This problem demonstrates closures and how let creates block-scoped bindings in loops.

Asked In

Understanding Closures

A closure is a function that has access to variables in its outer (enclosing) lexical scope, even after the outer function has returned.

The Closure in This Problem

function outer() {
  let count = 0;  // Outer scope variable
  return function inner() {
    count++;       // Inner function accesses outer variable
    console.log(count);
  }
}

The inner function forms a closure over count, maintaining access to it even after outer() has finished executing.

Execution Flow

1. outer() is called

2. count = 0 is initialized

3. Loop runs 5 times, creating 5 setTimeout callbacks

4. inner function is returned (with closure over count)

5. closureFunc() is called → increments count to 1, logs 1

Output: 1

About the setTimeout Callbacks

The setTimeout callbacks will execute later (after 1s, 2s, 3s, 4s, 5s), but they don't affect the immediate output of closureFunc().

Important: With let i, each loop iteration creates a new binding, so each setTimeout callback captures a different i value (1, 2, 3, 4, 5).

let vs var in Loops

With let (Block Scope)

for (let i = 1; i <= 5; i++) {
  setTimeout(function() {
    console.log(i); // Each callback sees different i: 1, 2, 3, 4, 5
  }, i * 1000);
}

Why? let creates a new binding for each iteration. Each setTimeout callback captures its own i value.

With var (Function Scope)

for (var i = 1; i <= 5; i++) {
  setTimeout(function() {
    console.log(i); // All callbacks see the same i: 6, 6, 6, 6, 6
  }, i * 1000);
}

Why? var has function scope, so there's only one binding shared across all iterations. By the time callbacks execute, the loop has finished and i = 6.

Visual Comparison

let i:
Iteration 1: i = 1 (new binding)
Iteration 2: i = 2 (new binding)
Iteration 3: i = 3 (new binding)
...

var i:
Iteration 1: i = 1 (same binding)
Iteration 2: i = 2 (same binding)
Iteration 3: i = 3 (same binding)
... (all share one i)

Closure Practical Applications

1. Data Privacy

function createCounter() {
  let count = 0; // Private variable
  return {
    increment: () => ++count,
    decrement: () => --count,
    getCount: () => count
  };
}

const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount();  // 2
// count is not directly accessible

2. Function Factories

function multiplyBy(factor) {
  return function(number) {
    return number * factor;
  };
}

const double = multiplyBy(2);
const triple = multiplyBy(3);

double(5);  // 10
triple(5);  // 15

3. Event Handlers

function setupButton(buttonId, clickCount) {
  document.getElementById(buttonId).addEventListener('click', function() {
    clickCount++;
    console.log(`Button clicked ${clickCount} times`);
  });
}

Each button maintains its own clickCount through closure.