Functions & Closures

1. Anatomy of a Function

There are three main ways to write functions in modern JavaScript.

A. Function Declaration

Hoisted to the top of their scope. You can call them before they are defined in the code.

// This works!
greet("Alice");

function greet(name) {
  console.log(`Hello, ${name}!`);
}

B. Function Expression

Not hoisted. You must define them before using them.

const add = function(a, b) {
  return a + b;
};

C. Arrow Functions (ES6)

Concise syntax. They do not have their own this context (which makes them great for callbacks).

2. The Prerequisite: Scope

To understand closures, you must understand Scope. Scope determines where variables are accessible.

  1. Global Scope: Accessible everywhere.

  2. Function Scope: Variables declared inside a function are hidden from the outside.

  3. Block Scope: Variables declared with let or const inside { } (like if-statements or loops) are trapped there.

3. What is a Closure?

A closure is a function that remembers the variables from the place where it was defined, even after that place (the parent function) has finished executing.

Think of a closure as a function with a backpack. In that backpack, it carries all the variables that were present in its parent scope when it was created.

The Basic Example

Why this works: Even though createCounter finished, myCounter holds a closure (a link) back to the count variable.

4. Practical Use Cases

A. Data Privacy (Encapsulation)

In other languages like Java, you have private variables. In JS, we use closures to hide data so it can't be modified directly from the outside.

B. Function Factories

You can create functions that create other functions with specific settings.

5. The Famous Loop Pitfall

A common interview question involves closures inside loops. It combines Scope (where variables live) with Asynchronous code (timing).

The Bug

Detailed Breakdown

1. The Timeline Mismatch

JavaScript runs the for loop logic synchronously (instantly), but the code inside setTimeout is asynchronous (it waits).

  1. Start: The loop begins. var i is 1.

  2. Iteration 1: JS sees setTimeout. It schedules a task: "Run this function in 1 second." It does not run the code inside the function yet.

  3. Iteration 2: i becomes 2. JS schedules another task.

  4. Iteration 3: i becomes 3. JS schedules a third task.

  5. Loop End: i becomes 4. The loop check i <= 3 fails. The loop terminates.

  6. ... 1 Second Passes ...

  7. Timeouts Execute: The three scheduled functions finally wake up and run.

2. The "Shared Reference" Problem (var)

When the functions inside setTimeout finally run, they need to print i.

  • They look up their closure "backpack" to find i.

  • Because var is function-scoped (not block-scoped), there was only ever one single variable named i created for that entire loop.

  • All three functions are pointing to that exact same spot in memory.

  • By the time they look at it, the loop has long finished, and the value sitting in that spot is 4.

The Fix: let

Using let creates a new binding (a new scope) for every iteration of the loop.

Why let Fixes It

let is block-scoped. When you use let in a for loop header, JavaScript does something special:

  • It creates a completely new variable for each specific iteration of the loop.

  • Iteration 1: A new variable i (value 1) is created. The first function closes over this specific variable.

  • Iteration 2: A new variable i (value 2) is created. The second function closes over this one.

  • Iteration 3: A new variable i (value 3) is created. The third function closes over this one.

When the functions wake up 1 second later, they each look at their own unique, private copy of i that was frozen in time when they were created.

Last updated