Functions are first-class values in JavaScript — passed as callbacks, returned from factories, and closed over outer variables (closures).
Core concepts
- Declaration vs expression:
function foo() {}hoists;const foo = () => {}does not. — MDN: Functions - Arrow functions: Lexical
this— prefer for callbacks; avoid as object methods when you need dynamicthis. — MDN: Arrow functions - Closures: Inner functions remember variables from outer scopes — used in debouncing, React hooks, and module patterns. — javascript.info: Closures
- ES modules:
import/export— static, tree-shakeable; Node supports them in.mjsor"type":"module"packages. — MDN: JavaScript modules
function makeCounter() {
let n = 0;
return () => ++n;
}
const next = makeCounter();
console.log(next(), next()); // 1, 2- Default & rest parameters:
function sum(a, b = 0, ...rest) {}. — MDN: Default parameters
Concept videos
YouTube lessons for these topics. On a large screen they open in the reading panel next to the roadmap, same as the article links on each core concept.
- Closures — Closures (Web Dev Simplified)
- ES modules — JavaScript Crash Course (Traversy Media)
Explanation
Functions are reusable verbs in your program. JavaScript treats them like any other value — you can store them in variables, pass them to other functions, and return them from factories. That design is why callbacks, promises, and React hooks all feel similar once you see the pattern.
Declaration vs expression
A function declaration is hoisted: the engine registers the whole function before it runs your first line, so you can call foo() above its definition. A function expression assigned to const behaves like other variables — no use before initialization. Teams often use declarations for top-level helpers and expressions for callbacks to make temporal dead zone rules obvious.
Arrow functions
Arrow functions inherit this from the surrounding scope like a photograph of the room instead of a new room. That makes them perfect for array.map and event handlers in modern React. They are a poor fit for object methods that need their own this (for example, a click() method on a literal object) unless you bind explicitly.
Closures
A closure is a function carrying a backpack of variables from where it was created. The counter example keeps n private — nothing outside can reset it except the returned function. Real apps use closures for debouncing search, memoizing expensive work, and encapsulating module state before ES modules existed.
ES modules
Modules are labeled shipping crates: each file exports a public surface and imports only what it needs. Bundlers can tree-shake unused exports because import paths are static. Node’s "type": "module" or .mjs extension tells the runtime you are not using legacy require.
Default & rest parameters
Default parameters fill in missing arguments (b = 0). Rest parameters (...rest) gather the remainder into an array — ideal for logging wrappers and flexible APIs. Together they replace much of the old arguments object magic with readable syntax.
What Is It?
Functions define behavior; scope decides who can see which variables; closures let inner functions remember outer variables.
Arrow functions share lexical this, while regular functions get this from how they are called — a common interview theme tied to this chapter.
ES modules split code into files with explicit imports and exports, enabling tree-shaking and clear dependencies.
Why Should I Care?
Callbacks, React hooks, and middleware are closures in disguise. Without scope fluency, async code looks like magic.
Module boundaries are how teams prevent 'everything is global' spaghetti in large repos.
Real World Analogy
A closure is a backpack a function carries home: it still holds variables from the classroom where it was created, even after class ends.
Visual Mental Model
outer scope
|-- let n
|-- function inner() { uses n }
inner keeps link to n (closure)Beginner Explanation
Functions are values — pass them to map, return them from factories, store them in objects. ES modules (import/export) replace global script tags for anything serious.
Smallest Possible Example
export function makeCounter() {
let n = 0;
return () => ++n;
}
const next = makeCounter();
console.log(next(), next());Common Mistakes
❌ Creating functions inside loops with var and expecting unique indices.
Classic bug: all callbacks see the final index. Use let or pass the index as a parameter.
❌ Using arrow functions as object methods when you need dynamic this.
Arrows do not bind their own this. Methods that need the object often use regular functions.
❌ Circular imports without a plan.
Two modules importing each other can yield partially initialized exports. Restructure shared code into a third module.
Real Project Example
Express middleware chains are higher-order functions passed into app.use.
React function components close over props and state on every render — that is closure mechanics.
Build This Mini Project
Build a tiny module math.js exporting add, import it from main.mjs, then refactor to a factory makeCalculator() that returns an object of methods sharing private state.
Interview Question
What is a closure?
A closure is when a function retains access to variables from its lexical scope even after that outer function has finished executing. Practically, it is how you build private state, partial application, and many event handlers.
Key Takeaways
- Functions are first-class values.
- Closures power callbacks and factories.
- Prefer ES modules over global scripts.
- Know arrow vs regular function
thisbehavior.
Resources
The 60-second story
A food truck has one cook and a stack of order slips. Some slips are printed at opening and taped above the grill. Those exist even before the lunch rush. Other slips are written when a customer walks up. Those do not exist until the moment you write them.
That is declaration versus expression. function fry() is the printed slip, available early because JavaScript hoists the name. const fry = () => {} is the slip you write when that line runs. Call it too early and there is no slip yet. Arrow functions are the short slips you hand to someone else: “when the timer dings, run this.” They keep the this from the room they were written in. A regular function looks around the room it is called in, which is why arrows make bad object methods when the method needs “this truck,” not “the place I was born.”
Now the part people feel in their bones. The cook writes a slip that says “remember how many burgers we have sold.” That slip leaves the truck in a courier’s pocket and still knows the count. The pocket is a closure. The function traveled. The variables it needed traveled with it, like a backpack.
Map the truck.
- Printed opening menu = a function declaration.
- A slip written at the moment of the order = a function expression.
- A short slip that keeps the room it was written in = an arrow function.
- The backpack of remembered numbers = a closure.
- Handing a sealed recipe to another truck =
export. - Accepting that recipe =
import. - “If they forget the sauce, use mild” = a default parameter.
- “And whatever extra toppings they shout” = rest parameters.
function makeCounter() {
let sold = 0;
return () => ++sold;
}
const nextBurger = makeCounter();
nextBurger();
nextBurger();nextBurger is not a copy of the number. It is a function carrying sold in a backpack. Call it twice and the count is 2, even though sold is not a global sock left on the floor. Globals are how you lose track of whose pair is whose. Closures keep the pair in the bag.
Modules exist because a single file named utils.js that everyone edits becomes a junk drawer. import and export are labeled boxes. The wiring is static, so a bundler can throw away boxes nobody opened. Node accepts that style in .mjs or when package.json says "type": "module".
Default and rest parameters exist so you stop writing if (sauce === undefined) sauce = 'mild' at the top of every function. The signature tells the truth.
Why functions are first-class: JavaScript needed to hand behavior around, not just numbers. Callbacks, event handlers, and React hooks are all slips you pass to someone else. Without closures, those slips would forget the truck they came from. That is not poetry. That is a bug.
Mental model: a backpack that rides with the function. The function can leave the room. The variables it closed over do not fall out on the sidewalk.
If you can say “arrows keep their birthplace this, closures are backpacks, modules are labeled boxes,” you can explain this page on a whiteboard without opening the docs.

