Notifications

No notifications

/Phase 2

Functions & Arrow Functions

Functions in JavaScript are *first-class* — you can pass them as arguments, return them, and store them in variables and arrays. This page covers the four ways to create them (function declarations, expressions, arrow functions, methods), the modern essentials (default params, rest, spread), and the two everyday gotchas: this binding and closures.

On this page

Detailed Theory

# Functions

Four ways to create one

1. Function declaration — hoisted

function add(a, b) { return a + b; }
Hoisted to the top of its scope, so you can call it before its definition.

2. Function expression — not hoisted

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

3. Arrow function — concise + lexical this

const add = (a, b) => a + b;
const sq  = x => x * x;          // single param, parens optional
const fn  = () => ({ ok: true }); // wrap returned object literal in parens

4. Object method shorthand

const calc = {
    add(a, b) { return a + b; },           // method shorthand
    sub: (a, b) => a - b,                   // arrow as a property
};

Default parameters

function greet(name = "world", punct = "!") {
    return hello, ${name}${punct};
}
greet();                  // "hello, world!"
greet("alice", ".");      // "hello, alice."

Rest parameters — collect into an array

function sum(...nums) {                    // nums is a real array
    return nums.reduce((s, n) => s + n, 0);
}
sum(1, 2, 3, 4);          // 10

Spread arguments — opposite of rest

const args = [1, 2, 3];
sum(...args);             // 6
Math.max(...[5, 9, 1]);   // 9

arguments object (legacy — avoid in arrow functions)

Inside *non-arrow* functions, arguments is an array-like of all passed args. Arrow functions don't have it — use rest ... instead.

Higher-order functions — functions are values

function apply(fn, x) { return fn(x); }
apply(sq, 5);                              // 25

// returning a function const multiplier = n => x => x * n; const triple = multiplier(3); triple(4); // 12

Closure — a function "remembers" its lexical scope

function makeCounter() {
    let count = 0;
    return () => ++count;
}
const c = makeCounter();
c(); c(); c();      // 1, 2, 3
The inner arrow keeps a reference to count even after makeCounter has returned. Closures are the foundation of private state, partial application, memoisation, and most React hooks.

Arrow functions and this — THE big difference

  • A regular function gets its own this based on how it's called (obj.fn() → obj, plain fn() → undefined in strict, new fn() → new instance).
  • An arrow function has no this — it captures the this from the surrounding lexical scope.
const counter = {
    n: 0,
    bad: function () { setTimeout(function () { this.n++; }, 100); },     // ❌ this = undefined/window
    good: function () { setTimeout(() => { this.n++; }, 100); },           // ✅ arrow inherits this from good()
};

When NOT to use an arrow function

  • Object methods that need thisobj.fn = () => this captures the *outer* this, not obj.
  • Constructors — arrows can't be used with new.
  • Generators — must be function*.

IIFE — Immediately Invoked Function Expression

A historical pattern (pre-modules) for private scope.
(function () {
    // private scope
    const secret = 42;
    console.log(secret);
})();
With ES modules you almost never need IIFEs anymore — every module *is* a private scope.