Topics in this subject
JavaScript 4 min read Updated 4 Aug 2026

Part 9 — Functions

Ways to define, Arrow vs regular — the differences that matter 🎯, Parameters: defaults, rest, destructuring, arguments, Higher-order functions, callbacks, pure/impure

Functions are first-class values: assign them, pass them, return them.

Ways to define#

function decl(a, b) { return a + b; }          // declaration — hoisted
const expr = function (a, b) { return a + b; }; // expression — not hoisted
const arrow = (a, b) => a + b;                  // arrow — concise, lexical this
const named = function fac(n){ return n<2?1:n*fac(n-1); }; // named expr (self-ref)
(function(){ console.log("IIFE"); })();          // runs immediately

Arrow vs regular — the differences that matter 🎯#

Regular function Arrow function
this dynamic (depends how it's called) lexical (inherits from enclosing scope)
arguments object yes no (use rest ...args)
Usable as constructor (new) yes no
Has own prototype yes no
Hoisted if declaration no
Can be generator yes no
const obj = {
  name: "X",
  regular() { return this.name; },     // "X"
  arrow: () => this.name,              // undefined — this is the outer scope
};

🟢 Use arrows for callbacks and when you want to keep the surrounding this (e.g., inside a class method's setTimeout). Use regular functions for object methods and anything used with new.

Parameters: defaults, rest, destructuring#

function connect({ host = "localhost", port = 5432 } = {}) { /* ... */ }
connect();                     // uses all defaults thanks to `= {}`
function log(level, ...msgs) { }  // rest gathers remaining args

⚠️ Default params are evaluated at call time, left to right, and can reference earlier params: (a, b = a * 2) => {}.

arguments#

Legacy array-like (not a real array) available in non-arrow functions. 🟢 Prefer rest params ...args, which give a real array.

Higher-order functions, callbacks, pure/impure#

  • Higher-order — takes and/or returns functions (map, setTimeout, debounce).
  • Callback — a function passed to be called later.
  • Pure — same input → same output, no side effects. Easy to test and cache.
  • Impure — depends on or changes outside state (I/O, Date.now(), mutation).
// pure
const double = x => x * 2;
// impure (reads external clock, logs)
const stampedLog = msg => console.log(Date.now(), msg);

Closures — the crown jewel 🎯#

ELI12. A closure is a function that remembers the variables from where it was born, even after that place has finished running. Like a backpack the function carries around with the outer variables inside.

function counter() {
  let count = 0;                 // private state
  return () => ++count;          // inner fn closes over `count`
}
const next = counter();
next(); // 1
next(); // 2  — `count` survived because the returned fn still references it

Why it exists / when to use: data privacy (module pattern), function factories, memoization, event handlers with retained state, currying.

graph LR
    subgraph "counter() scope (kept alive)"
    C["count = 2"]
    end
    N["next (the returned function)"] -->|closes over| C

⚠️ Closure traps:

  • The loop bug in Part 4 — var shares one binding.
  • Closures keep their entire scope reachable → memory leaks if a long-lived closure holds a big object it no longer needs.

Lexical environment#

Each execution creates a lexical environment: a record of local variables + a reference to the parent environment. The chain of these is the scope chain; closures are simply functions holding onto their lexical environment.

this — the four rules 🎯#

this is decided by how a function is called, not where it's defined (except arrows). In order:

  1. new bindingnew Foo()this is the new object.
  2. Explicit bindingf.call(obj), f.apply(obj), f.bind(obj)this is obj.
  3. Implicit bindingobj.f()this is obj.
  4. Default — plain f()this is undefined (strict) or global (sloppy).
  5. Arrow — ignores all the above; uses the enclosing lexical this.
function who() { return this?.name; }
const a = { name: "A", who };
a.who();                 // "A"  (implicit)
const loose = a.who;
loose();                 // undefined (default) ⚠️ lost `this`
who.call({ name: "C" }); // "C"  (explicit)

⚠️ Passing a method as a callback (setTimeout(a.who, 0)) loses this. Fix with .bind(a), an arrow wrapper, or a class field arrow.

call, apply, bind#

f.call(thisArg, a, b);     // invoke now, args listed
f.apply(thisArg, [a, b]);  // invoke now, args as array
const g = f.bind(thisArg, a); // returns a NEW function, partially applied

Currying, partial application, composition, memoization#

// currying: f(a)(b)(c)
const curry = f => a => b => c => f(a, b, c);

// partial application: fix some args now
const add = (a, b) => a + b;
const add10 = add.bind(null, 10);   // add10(5) → 15

// composition: pipe data through functions
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
const pipe    = (...fns) => x => fns.reduce((v, f) => f(v), x);
const shout = pipe(s => s.trim(), s => s.toUpperCase(), s => s + "!");
shout("  hi ");  // "HI!"

// memoization: cache by args
function memoize(fn) {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const val = fn(...args);
    cache.set(key, val);
    return val;
  };
}

Generators & async functions (intro; more in Part 20/16)#

function* ids() { let i = 0; while (true) yield i++; }
const gen = ids();
gen.next().value;  // 0
gen.next().value;  // 1  — generators pause at yield and resume on next()

async function load() { const r = await fetch("/api"); return r.json(); }

Interview questions (Part 9):

  1. Define a closure and give a real use (privacy, factory, memoize).
  2. What are the four this binding rules? How do arrows differ?
  3. Difference between call, apply, bind?
  4. Predict output when a method is detached and called standalone.
  5. Implement once(fn) so fn runs at most once (uses a closure).

Practice:

  • Implement curry, debounce, throttle, and memoize from scratch.
  • Build a private counter module using closures with increment, decrement, value.