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.
Global Scope: Accessible everywhere.
Function Scope: Variables declared inside a function are hidden from the outside.
Block Scope: Variables declared with
letorconstinside{ }(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).
Start: The loop begins.
var iis 1.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.Iteration 2:
ibecomes 2. JS schedules another task.Iteration 3:
ibecomes 3. JS schedules a third task.Loop End:
ibecomes 4. The loop checki <= 3fails. The loop terminates.... 1 Second Passes ...
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
varis function-scoped (not block-scoped), there was only ever one single variable namedicreated 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
letUsing 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