JavaScript Default Parameter Initializers Explained

Default parameter initializers in JavaScript allow function parameters to be set to predetermined values if no argument or undefined is passed during invocation. Introduced in ECMAScript 2015 (ES6), this feature simplifies function signatures and eliminates the need for manual fallback checks within function bodies. This article explains what default parameter initializers are, the rules governing when and how they are evaluated, and common patterns associated with their use.

What Are Default Parameter Initializers?

In JavaScript, function parameters default to undefined. Default parameter initializers allow you to assign a fallback value directly in the function signature using the assignment operator (=).

function greet(name = "Guest") {
  return `Hello, ${name}!`;
}

console.log(greet("Alice")); // Output: Hello, Alice!
console.log(greet());        // Output: Hello, Guest!
console.log(greet(undefined)); // Output: Hello, Guest!

The undefined Rule

A default initializer is triggered only when an argument is omitted or explicitly passed as undefined. Passing any other falsy value—such as null, false, 0, or ""—will not trigger the default value.

function displayCount(count = 10) {
  return count;
}

console.log(displayCount(null)); // Output: null
console.log(displayCount(0));    // Output: 0
console.log(displayCount());     // Output: 10

When Are Default Initializers Evaluated?

Default parameter initializers are evaluated at call time (runtime), not at the time the function is defined or parsed.

Because they are evaluated dynamically each time the function is called without that parameter: - A new instance or evaluation occurs on every relevant function call. - Initializers can execute expressions, call other functions, or instantiate new objects.

let counter = 0;

function getNextId() {
  return ++counter;
}

function createUser(id = getNextId()) {
  return { id };
}

console.log(createUser()); // Output: { id: 1 }
console.log(createUser()); // Output: { id: 2 }
console.log(createUser(99)); // Output: { id: 99 } (getNextId is not called)

In the example above, getNextId() is only executed when createUser is called without an argument.

Evaluation Order and Parameter Scope

Default parameters are evaluated sequentially from left to right. This behavior creates specific scope and ordering rules:

1. Referencing Preceding Parameters

Parameters to the right can reference parameters declared to their left:

function buildRectangle(width, height = width * 2) {
  return { width, height };
}

console.log(buildRectangle(5)); // Output: { width: 5, height: 10 }

2. Temporal Dead Zone (TDZ)

A parameter cannot reference another parameter declared to its right. Attempting to do so results in a ReferenceError due to the Temporal Dead Zone:

// Throws ReferenceError: Cannot access 'b' before initialization
function invalidScope(a = b, b = 2) {
  return a + b;
}

invalidScope();

3. Separate Parameter Scope

When a function uses default parameters, an intermediate scope is created between the outer scope and the function’s internal body scope. Variables declared inside the function body with let, const, or var are not accessible inside default parameter expressions.

const value = "outer";

function testScope(param = value) {
  let value = "inner";
  return param;
}

console.log(testScope()); // Output: "outer"

Key Takeaways