Topic 04 / 15

Functions, Scope & Closures

~11 min read  //  JavaScript Series  //  Coding India

Three Ways to Write a Function

// 1. Declaration — hoisted (usable before its definition)
function add(a, b) {
  return a + b;
}

// 2. Function expression
const subtract = function (a, b) {
  return a - b;
};

// 3. Arrow function — the modern default
const multiply = (a, b) => a * b;          // implicit return
const square = n => n * n;                  // one param, no parens needed
const greet = (name) => {
  const msg = `Hello ${name}`;              // braces = explicit return
  return msg;
};

Arrows are shorter and — crucially — don’t have their own this (they inherit it from the surrounding scope). That makes them ideal for callbacks; we’ll revisit this in the classes topic.

Default, Rest & Spread

function connect(host, port = 5432) { ... }   // default parameter

function sum(...nums) {                        // rest — gathers into an array
  return nums.reduce((acc, n) => acc + n, 0);
}
sum(1, 2, 3);          // 6

const nums = [1, 2, 3];
Math.max(...nums);     // spread — unpacks an array into arguments

Functions Are Values

Pass them, return them, store them — this is the heart of JS:

const byScore = (a, b) => b.score - a.score;
players.sort(byScore);

setTimeout(() => console.log("2s later"), 2000);   // callback

Scope

let/const are block-scoped — they exist only inside the nearest { }:

if (true) {
  const secret = 42;
}
console.log(secret);    // ReferenceError

Inner scopes can read outer variables. That plus first-class functions gives us…

Closures — Functions That Remember

A closure is a function that captures variables from the scope where it was created — and keeps them alive after that scope returns:

function makeCounter() {
  let count = 0;                 // private — nothing outside can touch it
  return function () {
    count++;
    return count;
  };
}

const counter = makeCounter();
counter();    // 1
counter();    // 2

const другой = makeCounter();
другой();     // 1 — each call gets its OWN count

Closures power debounce, event handlers, module patterns, React hooks — most of practical JS:

function debounce(fn, delay) {
  let timer;                              // remembered between calls
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}

const onType = debounce(() => search(input.value), 300);

If you can explain makeCounter from memory, you understand closures — a guaranteed interview question.