JavaScript Interview Prep
ES6+ Features

Default Parameters

Clean Defaults Without Falsy Traps

LinkedIn Hook

You ship this helper: function fee(amount) { amount = amount || 10; return amount * 0.05; }.

QA files a bug: "When I pass 0, the fee is 0.5, not 0."

Because 0 || 10 is 10. And "" || "Guest" is "Guest". And false || true is true. The classic falsy-OR trick quietly clobbers every legitimate falsy value your API actually cares about.

ES6 default parameters fix this with one rule: they trigger only when the argument is undefined. Not null. Not 0. Not "". Not false.

In this lesson you'll learn the exact trigger rule, how later parameters can reference earlier ones, how defaults compose with destructuring, and the TDZ edge case that trips up senior engineers.

If you still write x = x || default — this is the lesson that retires that pattern.

Read the full lesson -> [link]

#JavaScript #InterviewPrep #ES6 #DefaultParameters #Frontend #CodingInterview #CleanCode


Default Parameters thumbnail


What You'll Learn

  • The undefined-only trigger rule and why it beats the || fallback idiom
  • Referencing earlier parameters (left-to-right evaluation, TDZ for later names)
  • Default parameters combined with destructuring — the canonical ({ ... } = {}) pattern

Why Defaults Matter

Before ES6, handling default values meant writing param = param || fallback — which broke on falsy values like 0, "", and false. Default parameters provide a clean, correct solution. Think of them like a restaurant order: "I'll have the special, unless I specify otherwise."

Basic Syntax

// ES5 way — broken for falsy values
function greetOld(name) {
  name = name || "Guest";  // "" and 0 would also trigger default
  return `Hello, ${name}!`;
}

// ES6 default parameters — only triggers on undefined
function greet(name = "Guest") {
  return `Hello, ${name}!`;
}

greet("Rakibul"); // "Hello, Rakibul!"
greet();          // "Hello, Guest!"
greet(undefined); // "Hello, Guest!" — undefined triggers default
greet(null);      // "Hello, null!"  — null does NOT trigger default
greet("");        // "Hello, !"      — empty string does NOT trigger
greet(0);         // "Hello, 0!"     — 0 does NOT trigger

Expressions as Defaults

Default values can be any expression — including function calls:

function getDefaultAge() {
  console.log("Computing default age...");
  return 25;
}

function createUser(name = "Guest", age = getDefaultAge()) {
  return { name, age };
}

createUser("Rakibul", 30); // getDefaultAge() is NOT called
createUser("Rakibul");     // getDefaultAge() IS called — lazy evaluation

Accessing Earlier Parameters

Later parameters can reference earlier ones:

function createRange(start = 0, end = start + 10, step = 1) {
  return { start, end, step };
}

createRange();        // { start: 0, end: 10, step: 1 }
createRange(5);       // { start: 5, end: 15, step: 1 }
createRange(5, 20);   // { start: 5, end: 20, step: 1 }

// But earlier params CANNOT reference later ones
function broken(a = b, b = 1) {} // ReferenceError — TDZ for 'b'

Default with Destructuring

// Destructured object with defaults
function createUser({ name = "Guest", age = 25, role = "user" } = {}) {
  return { name, age, role };
}

createUser({ name: "Rakibul" }); // { name: "Rakibul", age: 25, role: "user" }
createUser();                     // { name: "Guest", age: 25, role: "user" }

// Array destructuring with defaults
function first([a = 0, b = 0] = []) {
  return a + b;
}

first([5, 3]); // 8
first([5]);    // 5
first();       // 0

Default Parameter Scope (TDZ)

Default parameters have their own scope — they exist in a middle ground between the outer scope and the function body:

let x = 1;

function foo(x = 2, y = () => x) {
  // The 'x' in y's closure is the parameter x, not the body x
  let x = 3; // This is a different x (function body scope)
  console.log(y()); // 2, not 3
}

foo(undefined); // Logs: 2 — y() captures parameter x, not body x

Default Parameters visual 1


Common Mistakes

  • Relying on x = x || default from ES5 muscle memory — that swaps in the default for every falsy value, silently corrupting 0, "", and false inputs. Default parameters fire only on undefined.
  • Referencing a parameter that hasn't been evaluated yet, like function f(a = b, b = 1) { ... } — parameters evaluate left-to-right, and b is in its TDZ when a's default runs. Always put the referenced parameter first.
  • Forgetting the = {} safety net on destructured parameters — function f({ a = 1 }) throws when called with no argument, because you can't destructure undefined. Write function f({ a = 1 } = {}).

Interview Questions

Q: When does a default parameter get used?

A default parameter is only used when the argument is undefined — either not passed or explicitly passed as undefined. Other falsy values (null, 0, "", false) do NOT trigger the default.

Q: Can a default parameter reference another parameter?

Yes, a later parameter can reference an earlier one (function f(a = 1, b = a + 1)), but NOT the reverse. Parameters are evaluated left to right and the temporal dead zone (TDZ) applies, so referencing a later parameter throws a ReferenceError.

Q: What is the TDZ edge case with default parameters?

Default parameters have their own scope. A function like function f(x = 2, y = () => x) { let x = 3; return y(); } returns 2, not 3 — because y closes over the parameter x, not the body-scoped x. This is because default params create an intermediate scope.

Q: How do you safely destructure an options argument with defaults?

Combine destructuring defaults with a parameter default of {}: function f({ name = "Guest", age = 25 } = {}) { ... }. The outer = {} ensures the function works when called with no argument at all; the inner defaults handle missing properties.


Quick Reference — Cheat Sheet

DEFAULT PARAMETERS — QUICK MAP

Trigger rule:
  argument === undefined  -> default fires
  null, 0, "", false, NaN -> default does NOT fire

Syntax:
  function f(x = 10)                     -> primitive default
  function f(x = getDefault())           -> lazy expression default
  function f(a = 1, b = a + 1)           -> later references earlier
  function f({ x = 1 } = {})             -> destructuring + outer guard
  function f([a = 0, b = 0] = [])        -> array destructuring

Do not:
  function f(a = b, b = 1)               -> ReferenceError (b in TDZ)
  x = x || default                       -> breaks on 0, "", false

Previous: Arrow Functions -> Lexical this and Shorter Syntax Next: let, const, Block Scoping -> Patterns and Edge Cases


This is Lesson 9.3 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.

On this page