JavaScript25 min readBeginner

Functions, Arrows & Closures

Function declarations, expressions, arrow syntax, default parameters, and the closure model that powers React.

Three ways to define a function

javascript
// 1. Function declaration — hoisted, has a name
function add(a, b) {
  return a + b;
}

// 2. Function expression — assigned to a variable, NOT hoisted
const sub = function (a, b) {
  return a - b;
};

// 3. Arrow function — concise, no own `this`
const mul = (a, b) => a * b;

console.log(add(2, 3), sub(2, 3), mul(2, 3));

All three are functions. The differences are subtle but matter:

  • Declarations are hoisted — you can call them before they appear in the file.
  • Expressions and arrows are NOT hoisted — they exist only after the line that defines them.
  • Arrow functions don't have their own `this`. They inherit it from the surrounding scope. (This is the #1 reason arrows exist.)

Arrow function syntax shortcuts

javascript
// Single parameter — parens optional
const double = n => n * 2;

// Single expression body — no braces, implicit return
const square = n => n * n;

// Multiple statements — braces and explicit return
const describe = n => {
  const kind = n >= 0 ? "non-negative" : "negative";
  return `${n} is ${kind}`;
};

// Returning an object literal — wrap in parens to disambiguate from a block
const point = (x, y) => ({ x, y });

Default and rest parameters

javascript
function greet(name = "world") {
  return `Hello, ${name}!`;
}
console.log(greet());           // Hello, world!
console.log(greet("Ada"));     // Hello, Ada!

// Rest collects extra arguments into an array
function sum(...nums) {
  return nums.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3, 4));  // 10

Closures

A closure is a function that "remembers" the variables from the scope where it was defined, even after that scope has finished executing. Closures are not a quirk — they're the foundation of how nearly every modern JS pattern works (modules, callbacks, React hooks).

🎒
Real-life analogy — A function with a backpack
When you create a function inside another function, it packs the outer function's variables into a backpack and carries it forever. Even after the outer function returns and 'leaves the building', the inner function still has access to everything in its backpack.
Closure: a function carrying its scope around
When an inner function references variables from its outer scope, those variables stay alive — even after the outer function returns.
Global
no variables
Output
Call stack
empty
Empty global scope.
javascript
function makeCounter() {
  let count = 0;                 // local to makeCounter
  return function () {
    count += 1;                  // but the inner function still sees it
    return count;
  };
}

const tick = makeCounter();
console.log(tick(), tick(), tick()); // 1, 2, 3

const tick2 = makeCounter();
console.log(tick2());                // 1 — fresh, independent count

Why does this work? When `makeCounter` returns, its scope normally would be garbage-collected. But the inner function captured a reference to `count`, so the JS engine keeps that variable alive as long as the inner function exists. Each call to `makeCounter` creates a new private `count`.

Practical use: data privacy

javascript
function makeWallet(initial) {
  let balance = initial;
  return {
    deposit(amount) { balance += amount; },
    withdraw(amount) {
      if (amount > balance) throw new Error("insufficient");
      balance -= amount;
    },
    getBalance() { return balance; },
  };
}

const w = makeWallet(100);
w.deposit(50);
console.log(w.getBalance()); // 150
// w.balance is undefined — there\u2019s no way to touch it from outside
💡 Tip
If you understand closures, you understand 80% of "why does my React component see stale state?" debugging. The closure captured the old value.