Chapter 1.3☕ 15 min read

Functions & Closures

Function samjho to JavaScript ka 70% samajh gaye — baaki closure ka jaadu hai.

01Function Declarations & Expressions

JavaScript mein function banane ke 3 main tareeqe hain, aur har ek ka behaviour alag hai:

// 1. FUNCTION DECLARATION — fully hoisted ✅
console.log(greet("Sai")); // "Hello Sai" — works BEFORE definition!
function greet(name) {
  return "Hello " + name;
}

// 2. FUNCTION EXPRESSION — assigned to variable
// console.log(add(2, 3)); // ❌ ReferenceError! (TDZ with let/const)
const add = function(a, b) {
  return a + b;
};

// 3. ARROW FUNCTION — shortest syntax
const multiply = (a, b) => a * b;
const double = x => x * 2;
const shout = () => "HYDERABAD!";
Hoisting difference: Function declarations are hoisted with their entire body — you can call them before they appear in code. Function expressions (with let/const) hit the Temporal Dead Zone — accessing them before the line throws ReferenceError. This is a very common interview question.

Named vs Anonymous expressions:

// Anonymous (most common)
const add = function(a, b) { return a + b; };

// Named — useful for recursion & stack traces
const factorial = function fact(n) {
  return n <= 1 ? 1 : n * fact(n - 1);
};
console.log(factorial(5)); // 120
// Note: "fact" is only available INSIDE the function
When to use what: Declarations for main utilities and named functions. Expressions when assigning to variables, passing as callbacks. Arrows for short callbacks — but never when you need this or arguments (covered in Section 5).
02Parameters & Arguments

Functions ke parameters mein bahut flexibility hai JavaScript mein:

// DEFAULT PARAMETERS
function greet(name = "Bhai") {
  return "Hello " + name;
}
console.log(greet());        // "Hello Bhai"
console.log(greet("Sai"));  // "Hello Sai"

// REST PARAMETERS — collects all args into array
function sum(...nums) {
  return nums.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3, 4)); // 10

// Mix regular + rest
function log(level, ...messages) {
  console.log(`[${level}]`, messages.join(" "));
}
Rest vs arguments: ...args gives a real Array (map, filter, reduce work). The arguments object is array-like but NOT a real Array — and it only exists in regular functions, NOT arrow functions.
// arguments object — ONLY in regular functions
function showArgs() {
  console.log(arguments);    // { 0: 'a', 1: 'b', length: 2 }
  console.log(arguments[0]); // 'a'
}

const arrowArgs = () => {
  console.log(arguments); // ❌ ReferenceError in strict mode
  // Or points to OUTER function's arguments in sloppy mode
};

// DESTRUCTURED PARAMETERS
function user({ name, city = "Hyderabad" }) {
  return `${name} from ${city}`;
}
console.log(user({ name: "Sai" }));           // "Sai from Hyderabad"
console.log(user({ name: "Ravi", city: "Mumbai" })); // "Ravi from Mumbai"
📋 Pro Tip: Default parameters are evaluated at call time, not definition time. function f(arr = []) {} creates a NEW empty array each call — no shared reference bug unlike Python's mutable default args.
03Return & Scope

Return aur scope samajhna functions ka sabse important part hai:

// IMPLICIT undefined — function without return
function noReturn() {
  const x = 5;
  // no return statement
}
console.log(noReturn()); // undefined

// EARLY RETURN pattern — clean & readable
function findUser(users, id) {
  for (const user of users) {
    if (user.id === id) return user;  // exit early
  }
  return null;  // not found
}
// No deep nesting needed!
Early return rule: Guard clauses first (error checks, edge cases), then main logic. This keeps code flat instead of deeply nested if-else pyramids. Every senior developer writes this way.

Function Scope vs Block Scope:

function scopeDemo() {
  // var is FUNCTION-SCOPED — leaks out of blocks!
  var x = 1;
  if (true) {
    var x = 2; // SAME x — reassigns, not new variable
  }
  console.log(x); // 2

  // let is BLOCK-SCOPED — stays inside {}
  let y = 1;
  if (true) {
    let y = 2; // DIFFERENT y — only exists in this if block
  }
  console.log(y); // 1

  // for loop — var leaks, let doesn't
  for (var i = 0; i < 3; i++) {}
  console.log(i); // 3 — var i leaked out!

  for (let j = 0; j < 3; j++) {}
  // console.log(j); // ❌ ReferenceError — let j stayed in loop
}
Variable shadowing: An inner variable with the same name "shadows" the outer one. Inside the block, the inner one is used. Outside, the outer one is unaffected. This works with both var and let, but with let you get a clean new binding per block.
04Closures — The Secret Sauce

Yeh JavaScript ka sabse powerful concept hai. Dhyan se samjho — baaki sab closures pe based hai.

function createCounter() {
  let count = 0;  // ← This variable is "closed over"
  return function inner() {
    count++;
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
// count survives even though createCounter() finished long ago!
What is a closure? A closure is a function plus its surrounding lexical environment. When inner is created, it "remembers" the variables in its outer scope (count). Even after createCounter() returns and its execution context is popped off the stack, the inner function still has a reference to count. The garbage collector cannot free count because something is still using it.
// Each call creates a NEW closure with its own count
const counter1 = createCounter();
const counter2 = createCounter();

console.log(counter1()); // 1
console.log(counter1()); // 2
console.log(counter2()); // 1 — independent!
console.log(counter1()); // 3 — continues from 2
Private variables pattern: Closures let you create truly private data — no external code can directly access count. This is how JavaScript achieved encapsulation before classes with #private fields existed.
function createBankAccount(initial) {
  let balance = initial; // private — no direct access
  return {
    deposit(amount)  { balance += amount; },
    withdraw(amount) { if (amount <= balance) balance -= amount; },
    getBalance()     { return balance; },
  };
}
const acc = createBankAccount(1000);
acc.deposit(500);
acc.withdraw(200);
console.log(acc.getBalance()); // 1300
// console.log(acc.balance);    // undefined — truly private!
05Real World Patterns

Closures real-world mein har jagah use hote hain. Yeh patterns zyaada tar projects mein milte hain:

1. Module Pattern — private state ke saath:

const Calculator = (function() {
  let history = []; // private
  return {
    add(a, b) {
      const result = a + b;
      history.push(result);
      return result;
    },
    getHistory() { return [...history]; }, // return copy
  };
})();
Calculator.add(2, 3);
Calculator.add(10, 20);
console.log(Calculator.getHistory()); // [5, 30]
// console.log(Calculator.history); // undefined — private!

2. Currying — functions ko chain karo:

function multiply(a) {
  return function(b) {
    return a * b;
  };
}
const double  = multiply(2);
const triple  = multiply(3);
console.log(double(5));  // 10
console.log(triple(5));  // 15
// "a" is remembered via closure!

3. Event handlers — data remember karna:

function setupButton(label) {
  const btn = document.createElement("button");
  btn.textContent = label;
  btn.addEventListener("click", function() {
    console.log("Clicked:", label); // "label" remembered!
  });
  return btn;
}
// Each button's handler has its own closure with its own label
📋 Arrow vs Regular — `this` ka difference:
• Regular function: this = jo call karta hai (dynamic binding)
• Arrow function: this = jahan function likha gaya hai (lexical binding)
• Object methods mein arrow mat use karo — this galat hoga!
• Callbacks mein arrow use karo — outer this mil jayega!
const user = {
  name: "Sai",
  // ❌ Arrow — `this` is lexical (window/undefined in strict)
  greetWrong: () => console.log(this.name),
  // ✅ Regular — `this` is the object
  greetRight() { console.log(this.name); },
};
user.greetWrong(); // undefined
user.greetRight(); // "Sai"

Lo kar liya — Key Points:

  • ✅ Function declarations are fully hoisted — expressions hit TDZ with let/const
  • ✅ Arrow functions have no own this, no arguments object
  • var is function-scoped, let is block-scoped — this matters in loops!
  • ✅ Closure = function + its lexical environment — inner function remembers outer variables
  • ✅ Each function call creates a NEW closure — independent copies of closed-over variables
  • ✅ Use closures for private data, currying, and module pattern
  • ✅ Arrow for callbacks, regular for object methods — this rule
Course Search
Search across all chapters & stages
📖

Search the course

Type any topic — branching, stash, rebase, hooks — and jump straight to that chapter.

merge branchesgit stashundo commitrebase