Chapter 5.4☕ 19 min read

Parameters: Default, Rest, Destructured & Parameter TDZ

Inputs itne simple nahi — har default naya banta hai, aur TDZ mein panghe mat.

01Default Parameters — Sirf Undefined Pe Chale

Default parameters apply ONLY when the argument is undefined — not null, not 0, not false! This is the #1 source of confusion.

// 1. Only undefined triggers default
function greet(name, greeting = 'Hello') {
  console.log(greeting + ', ' + name + '!');
}
greet('Sai');            // 'Hello, Sai!' (default used)
greet('Sai', 'Namaste'); // 'Namaste, Sai!' (explicit value)
greet('Sai', undefined); // 'Hello, Sai!' (undefined triggers default)
greet('Sai', null);      // 'null, Sai!' (null does NOT trigger default!)

// 2. Evaluated at call time — new array each time!
function addToList(item, list = []) {
  list.push(item);
  return list;
}
console.log(addToList('a')); // ['a']
console.log(addToList('b')); // ['b'] — not ['a', 'b']! New [] each call.

// 3. Can reference earlier parameters
function createProfile(name, age, role = name + ' - ' + age) {
  return { name, age, role };
}
console.log(createProfile('Sai', 25));
// { name: 'Sai', age: 25, role: 'Sai - 25' }
02Parameter TDZ — Pehle Access Kiya Toh Game Over

Parameters have their own scope between the outer scope and the function body. This creates a Temporal Dead Zone (TDZ) for parameters declared after the one being evaluated.

// Parameter TDZ — accessing later param causes ReferenceError
function bad(x = y, y = 2) {
  console.log(x, y);
}
// bad(); // ReferenceError: Cannot access 'y' before initialization
// When x's default is evaluated, y is in the TDZ!

function good(x = 1, y = x) {
  console.log(x, y);
}
good();    // 1, 1 — x is evaluated first, y can see it
good(5);   // 5, 5 — x is 5, y defaults to x (5)

// Function body vars are NOT visible to defaults
function bodyScope(x = outer) {
  const outer = 'hello'; // This is in function body, not param scope!
}
// bodyScope(); // ReferenceError: outer is not defined
V8 creates a separate 'formal parameters' scope between the outer scope and the function body scope. This is why default parameters can see earlier parameters but not function body variables. It's a two-step scope chain: Outer → Parameter Scope → Function Body Scope.
03Rest Parameters — Modern arguments, Real Array

Rest parameters collect all remaining arguments into a real Array. They replace the old arguments object for modern code.

// Rest parameters — always an Array
function sum(base, ...numbers) {
  console.log(Array.isArray(numbers)); // true
  return numbers.reduce((acc, n) => acc + n, base);
}
console.log(sum(10, 1, 2, 3)); // 16 (10 + 1 + 2 + 3)
console.log(sum(0));            // 0  (numbers is [])

// Rest vs arguments
function showDifference(...rest) {
  console.log('Rest:', rest);
  // console.log('Arguments:', arguments); // Avoid in modern code!
}
showDifference('a', 'b', 'c');
// Rest: ['a', 'b', 'c'] — clean array

// Rest with other parameters
function log(level, ...messages) {
  messages.forEach(msg => console.log(`[${level}] ${msg}`));
}
log('ERROR', 'File not found', 'Retrying...');
// [ERROR] File not found
// [ERROR] Retrying...
04Destructured Parameters — Object Seedha Kholo

You can destructure objects and arrays directly in the parameter list — but there are traps to watch for!

// Destructured object parameter
function createUser({ name, age, role = 'user' }) {
  return { name, age, role };
}
console.log(createUser({ name: 'Sai', age: 25 }));
// { name: 'Sai', age: 25, role: 'user' } (inner default)

// Without = {} at end, calling f() throws TypeError!
// createUser(); // TypeError: Cannot destructure property 'name' of undefined

// Safe: outer default provides empty object if no argument
function safeCreateUser({ name = 'Anonymous', age = 0 } = {}) {
  return { name, age };
}
console.log(safeCreateUser());          // { name: 'Anonymous', age: 0 }
console.log(safeCreateUser({}));        // { name: 'Anonymous', age: 0 }
console.log(safeCreateUser({ name: 'Ali' })); // { name: 'Ali', age: 0 }

// Array destructuring
function getFirstTwo([first, second]) {
  return [first, second];
}
console.log(getFirstTwo([10, 20, 30])); // [10, 20]
05Strict Mode arguments — Copy Ya Alias?

The arguments object behaves differently in strict mode vs sloppy mode — and this trips up many developers.

// Sloppy mode — aliasing
function sloppyAliasing(a, b) {
  arguments[0] = 99;
  console.log(a);           // 99! arguments[0] changed 'a'
  console.log(arguments[1]); // 2
}
sloppyAliasing(1, 2);

// Strict mode — no aliasing
function strictAliasing(a, b) {
  'use strict';
  arguments[0] = 99;
  console.log(a);           // 1 — 'a' unaffected!
  console.log(arguments[0]); // 99 — arguments is a copy
}
strictAliasing(1, 2);

// Converting arguments to real array (old way)
function oldWay() {
  const args = Array.prototype.slice.call(arguments);
  // Or: const args = [...arguments];
  return args.map(x => x * 2);
}
📋 Always prefer rest parameters over the arguments object. Rest gives you a real Array, works in arrow functions, and avoids the aliasing confusion between strict and sloppy mode. The arguments object is a legacy feature kept only for backwards compatibility.

Lo kar liya — Key Points:

  • ✅ Default parameters apply only when argument is undefined; evaluated at call time so objects/arrays are fresh each call
  • ✅ Parameter TDZ: default params can reference earlier params but not later ones (ReferenceError)
  • ✅ Rest parameters (...args) collect remaining arguments into a real Array; must be the last parameter
  • ✅ Destructured parameters unpack objects/arrays; use = {} outer default to avoid TypeError on no-argument calls
  • ✅ In sloppy mode, arguments[i] aliases parameters; in strict mode, they are separate copies
  • ✅ Arrow functions have no arguments object; always use rest parameters
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