How to Use Default Parameters in JS Functions

Default parameters let you set fallback values when an argument is missing or undefined. Learn the syntax, rules, and practical patterns for cleaner function signatures.

6 min read

Default parameters let you set a fallback value directly in the function signature. If the caller omits an argument or passes undefined, the default takes over. No more manual typeof checks cluttering your function body.

Here is the simplest example:

javascriptjavascript
function greet(name = "Guest") {
  return `Hello, ${name}!`;
}
 
console.log(greet("Sara")); // "Hello, Sara!"
console.log(greet());        // "Hello, Guest!"

The equals-Guest part of the parameter list says: if name is missing or undefined, use "Guest" instead. That is the entire feature.

Before Default Parameters

Before ES6 introduced default parameters in 2015, developers handled missing arguments manually. If you are unclear on the difference between parameters and arguments, read JS Function Parameters vs Arguments Differences first.

Before default parameters existed, the common pattern was:

javascriptjavascript
function greet(name) {
  name = typeof name !== "undefined" ? name : "Guest";
  return `Hello, ${name}!`;
}

Some developers reached for a shorter version using the logical OR operator instead, since it looks like it does the same job in fewer characters:

javascriptjavascript
function greet(name) {
  name = name || "Guest"; // Bug: treats "" and 0 as missing too
  return `Hello, ${name}!`;
}

The OR-operator approach fails when the argument is a valid falsy value like 0, an empty string, or false. Default parameters solve this correctly, since only undefined triggers the fallback.

Only undefined Triggers the Default

This is the most important rule. Default parameters activate only when the argument is strictly undefined. Other falsy values pass through unchanged. For a refresher on how functions work, see What Is a Function in JavaScript Beginner Guide.

javascriptjavascript
function test(value = "default") {
  console.log(value);
}
 
test();           // "default"
test(undefined);  // "default"
test(null);       // null       (not default!)
test(0);          // 0          (not default!)
test("");         // ""         (not default!)
test(false);      // false      (not default!)

This is deliberate. Values like null, 0, and an empty string are meaningful and might be passed on purpose, so JavaScript only falls back to the default when it sees the true absence of a value, which is exactly what undefined represents. Silently replacing a real 0 or an empty string would hide bugs instead of preventing them.

Defaults Are Evaluated at Call Time

The default expression runs fresh every time the function is called. This is especially useful for arrays and objects as defaults:

javascriptjavascript
function addItem(item, list = []) {
  list.push(item);
  return list;
}
 
console.log(addItem("a")); // ["a"]
console.log(addItem("b")); // ["b"] -- fresh array, not ["a", "b"]

Each call creates a new empty array. If defaults were evaluated once at definition time, every call would share the same array, which would be a subtle and painful bug.

This also means function calls used as defaults run at each invocation, not once when the function is defined. That matters whenever the default value should reflect something that changes over time, such as a timestamp:

javascriptjavascript
function getTimestamp() {
  return Date.now();
}
 
function log(message, time = getTimestamp()) {
  console.log(`[${time}] ${message}`);
}
 
log("start");
// Wait a second...
log("end"); // Different timestamp

Referencing Earlier Parameters

Default parameters are not limited to fixed values. A default value can reference parameters defined to its left in the same parameter list, which lets one argument's default depend on another argument the caller already provided:

javascriptjavascript
function formatName(first, last, full = `${first} ${last}`) {
  return full;
}
 
console.log(formatName("Jane", "Doe"));        // "Jane Doe"
console.log(formatName("Jane", "Doe", "JD")); // "JD" (explicit overrides default)

This is powerful for computed defaults that depend on other arguments, since you can express a relationship between two parameters directly in the function signature instead of recalculating it in the function body:

javascriptjavascript
function createRange(start, end = start + 10) {
  return [start, end];
}
 
console.log(createRange(5));    // [5, 15]
console.log(createRange(5, 7)); // [5, 7]

The reverse does not work. A parameter cannot reference one defined to its right, because each parameter is initialized left to right and a later parameter does not exist yet when an earlier default runs:

javascriptjavascript
function broken(a = b, b = 1) {
  return a + b;
}
 
broken(); // ReferenceError: Cannot access 'b' before initialization

The error happens because the first parameter's default tries to read the second parameter before it has been set up. Parameters share the same left-to-right initialization order as let and const declarations, including the temporal dead zone that blocks early access.

Default Parameters and the arguments Object

When you use default parameters, the arguments object stops reflecting changes to the named parameters:

javascriptjavascript
function withDefaults(a, b = 2) {
  console.log(arguments[0], arguments[1]);
  a = 99;
  console.log(arguments[0]); // Still the original value
}
 
withDefaults(10);
// 10 undefined
// 10  (arguments[0] is still 10, not 99)

In strict mode or with default parameters, the arguments object is a snapshot of the call values, not a live view.

Defaults and function.length

Default parameters affect the function's length property, which counts the number of parameters before the first default:

javascriptjavascript
function f1(a, b) {}        // f1.length === 2
function f2(a, b = 1) {}    // f2.length === 1
function f3(a = 1, b) {}    // f3.length === 0
function f4(a, b = 1, c) {} // f4.length === 1

Everything from the first default onward is excluded from the count. This matters when libraries introspect function signatures.

Practical Patterns

Optional Configuration Objects

A common pattern combines default parameters with destructuring so a function can accept an optional options object, where each option falls back to a sensible value when the caller leaves it out:

javascriptjavascript
function createButton(text, { size = "md", color = "blue", disabled = false } = {}) {
  console.log(`Button: "${text}", size=${size}, color=${color}, disabled=${disabled}`);
}
 
createButton("Submit");
// Button: "Submit", size=md, color=blue, disabled=false
 
createButton("Delete", { color: "red", disabled: true });
// Button: "Delete", size=md, color=red, disabled=true

The empty-object default at the end is critical. Without it, calling createButton with only the text argument would throw a TypeError, because you cannot destructure a value that is undefined.

Required Parameter Pattern

You can use default parameters to enforce that an argument is provided:

javascriptjavascript
function required(name) {
  throw new Error(`Missing required parameter: ${name}`);
}
 
function createUser(name = required("name"), email = required("email")) {
  return { name, email };
}
 
createUser("Alex", "alex@example.com"); // Works
createUser(); // Error: Missing required parameter: name

This is more of a pattern than a built-in feature, but it uses default parameters creatively to simulate required arguments.

Common Mistake: Confusing null with undefined

Beginners often expect null to trigger a default value the same way a missing argument does, since both represent "nothing" in everyday language. JavaScript treats them differently: only undefined activates a default parameter.

javascriptjavascript
function show(value = "default") {
  console.log(value);
}
 
show(null); // null -- not "default"

Passing null on purpose is common when clearing a value, such as resetting a selection, so the language intentionally lets it through unchanged. If you want null to also trigger the default, you need to handle it in the function body or use the ?? nullish coalescing operator:

javascriptjavascript
function show(value) {
  value = value ?? "default";
  console.log(value);
}
 
show(null); // "default"

Default parameters alone do not catch null.

Where to Go Next

Default parameters replace verbose undefined checks with clean, self-documenting function signatures. They are evaluated fresh at each call, can reference earlier parameters, and only activate for undefined, not for null or other falsy values. Use them to make your functions more resilient and your code easier to read.

Once defaults feel comfortable, revisit how they interact with the rest of a function's parameter list, especially when mixing required arguments, defaulted arguments, and rest parameters in the same signature. Reading real function signatures in library code becomes much easier once you can spot a default value at a glance and reason about exactly when it applies.

Rune AI

Rune AI

Key Insights

  • Set a default with param = value directly in the function signature.
  • The default only triggers when the argument is undefined, not for null, 0, or ''.
  • Defaults are evaluated at call time, so each call gets a fresh array, object, or computed value.
  • Earlier parameters are available to later defaults: function f(a, b = a * 2).
  • Default parameters do not contribute to the function's .length property.
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between passing undefined and not passing an argument?

There is no difference for default parameters. Both trigger the default value. Passing null, 0, false, or an empty string does not trigger the default.

Can I use a function call as a default value?

Yes. The default expression is evaluated at call time, so you can call a function, create a new array, or compute any expression as a default.

Do default parameters work in arrow functions?

Yes. The syntax is identical: (a, b = 1) => a + b. Default parameters work in all function forms.

Conclusion

Default parameters replace verbose undefined checks with clean, self-documenting function signatures. They are evaluated fresh at each call, can reference earlier parameters, and only activate for undefined -- not for null or other falsy values. Use them to make your functions more resilient and your code easier to read.