JavaScriptDefault Parameters

Default Parameters

Before ES2015, the way to give a parameter a fallback was a manual check (x = x || 10) which was buggy when 0 or "" was a valid value. Modern default parameters fix this: they are concise, only triggered by undefined, and can even reference earlier parameters or call other functions.

The basic syntax

JS
function greet(name = "friend") {
  return "Hello, " + name;
}

console.log(greet());           // Hello, friend
console.log(greet("Ada"));      // Hello, Ada

The expression after = is the default value, evaluated when the parameter would otherwise be undefined.

Triggered by `undefined` only

Defaults only fire when the parameter is exactly undefined — either because the caller omitted it or passed undefined explicitly. Any other value, including null, 0, "" and false, is kept as-is.

JS
function show(value = "default") {
  console.log(value);
}

show();              // default
show(undefined);     // default
show(null);          // null
show(0);             // 0
show("");            // (empty string)
show(false);         // false
Why this is better than `||`
The old idiom `value = value || "default"` treats every falsy value (`0`, `""`, `false`) as "missing" and replaces it with the default. That is almost always a bug. Default parameters and [nullish coalescing](/javascript/nullish-coalescing) avoid this trap.
Defaults are evaluated at call time

The default expression runs once per call, not once when the function is defined. That means each call can get a fresh value — important for mutable defaults like arrays.

JS
function pushTo(list = []) {
  list.push(1);
  return list;
}

console.log(pushTo());   // [1]
console.log(pushTo());   // [1]   — a fresh array each time
console.log(pushTo());   // [1]

Compare to Python, where mutable defaults are a famous footgun. JavaScript dodges that by re-evaluating the default expression on every call.

Defaults can reference earlier parameters

Each parameter is evaluated left-to-right, and later defaults can use earlier names.

JS
function makeRange(start = 0, end = start + 10, step = 1) {
  const result = [];
  for (let i = start; i < end; i += step) result.push(i);
  return result;
}

console.log(makeRange());           // 0..9
console.log(makeRange(5));          // 5..14
console.log(makeRange(0, 6, 2));    // [0, 2, 4]

Trying to reference a later parameter, on the other hand, is a TDZ error:

JS
// function bad(a = b, b = 2) { return a + b; }
// bad();   // ReferenceError: Cannot access 'b' before initialization
Defaults can be any expression

A default value is not limited to literals. It can be a function call, an object, or anything else that produces a value.

JS
function newId() {
  return Math.random().toString(36).slice(2, 8);
}

function makeUser(name, id = newId(), createdAt = new Date()) {
  return { id, name, createdAt };
}

console.log(makeUser("Ada"));
console.log(makeUser("Lin"));
Each call sees a fresh `newId()` and `new Date()`
Defaults are not memoised. Every call without those arguments runs the expression again — which is usually exactly what you want.
Required parameters via defaults

Because defaults are arbitrary expressions, a common trick is to default to a function that throws — making the parameter effectively required.

JS
function required(name) {
  throw new Error("Missing required parameter: " + name);
}

function createUser(name = required("name"), email = required("email")) {
  return { name, email };
}

createUser("Ada", "ada@example.com");   // ok
// createUser("Ada");                   // Error: Missing required parameter: email
Defaults with destructured parameters

Defaults combine nicely with object destructuring to give a function a clean "named arguments" API.

JS
function fetchData({ url, method = "GET", timeout = 5000 } = {}) {
  console.log(method, url, "timeout", timeout);
}

fetchData({ url: "/api/users" });
fetchData({ url: "/api/users", method: "POST" });
fetchData();   // {} — destructuring would crash without the `= {}`

The = {} at the end protects you when the caller omits the whole argument. Without it, destructuring undefined throws.