How to Use Default Parameters in JS Functions

Learn how to use default parameters in JavaScript functions. Covers ES6 default syntax, expressions as defaults, pre-ES6 patterns, how undefined triggers defaults but null does not, and practical patterns for config objects and optional settings.

JavaScriptbeginner
10 min read

Default parameters let you assign fallback values to function parameters when no argument is passed. Before ES6, developers used || or manual undefined checks to achieve this. ES6 default parameters provide a cleaner, built-in syntax that evaluates the default only when the argument is undefined.

This tutorial covers the full range of default parameter behavior, from basic syntax to advanced patterns like expressions as defaults and referencing other parameters.

The Pre-ES6 Approach

Before default parameters existed, developers used the || operator or explicit checks:

javascriptjavascript
// Pattern 1: OR operator (has a bug)
function greet(name) {
  name = name || "Guest";
  return `Hello, ${name}!`;
}
 
console.log(greet("Alice")); // "Hello, Alice!"
console.log(greet());        // "Hello, Guest!"
console.log(greet(""));      // "Hello, Guest!" (BUG: empty string is falsy)

The || pattern fails with falsy values like "", 0, false, and null. It treats all of them as missing:

javascriptjavascript
// Pattern 2: explicit undefined check (safer but verbose)
function greet(name) {
  if (name === undefined) {
    name = "Guest";
  }
  return `Hello, ${name}!`;
}
 
console.log(greet(""));  // "Hello, !" (empty string preserved)
console.log(greet(0));   // "Hello, 0!" (zero preserved)
console.log(greet());    // "Hello, Guest!"
PatternHandles undefinedHandles ""Handles 0Clean?
name || "default"YesTreats as missingTreats as missingShort
name === undefinedYesPreservesPreservesVerbose
name ?? "default"YesPreservesPreservesShort
ES6 default parameterYesPreservesPreservesBest

ES6 Default Parameter Syntax

The = sign after a parameter name assigns a default value:

javascriptjavascript
function greet(name = "Guest") {
  return `Hello, ${name}!`;
}
 
console.log(greet("Alice")); // "Hello, Alice!"
console.log(greet());        // "Hello, Guest!"
console.log(greet(undefined)); // "Hello, Guest!" (explicit undefined triggers default)

Multiple Default Parameters

javascriptjavascript
function createUser(name = "Anonymous", role = "viewer", active = true) {
  return { name, role, active };
}
 
console.log(createUser());
// { name: "Anonymous", role: "viewer", active: true }
 
console.log(createUser("Alice"));
// { name: "Alice", role: "viewer", active: true }
 
console.log(createUser("Bob", "admin"));
// { name: "Bob", role: "admin", active: true }
 
console.log(createUser("Charlie", "editor", false));
// { name: "Charlie", role: "editor", active: false }
Put Defaults Last

Parameters with defaults should come after required parameters. If a default parameter comes first, you must pass undefined to skip it: createUser(undefined, "admin"). This is confusing. Put required parameters first.

What Triggers the Default Value

Only undefined triggers the default. Any other value, including null, 0, "", and false, is used as-is:

javascriptjavascript
function demo(value = "default") {
  return value;
}
 
console.log(demo());          // "default" (no argument = undefined)
console.log(demo(undefined)); // "default" (explicit undefined)
console.log(demo(null));      // null (NOT replaced)
console.log(demo(0));         // 0 (NOT replaced)
console.log(demo(""));        // "" (NOT replaced)
console.log(demo(false));     // false (NOT replaced)
ArgumentTriggers default?Result
(nothing)Yes"default"
undefinedYes"default"
nullNonull
0No0
""No""
falseNofalse

This is a critical difference from the || pattern, which replaces all falsy values.

Expressions as Default Values

Defaults are not limited to simple values. Any expression works, and it is evaluated at call time, not at definition time:

javascriptjavascript
function createId(prefix = "id", timestamp = Date.now()) {
  return `${prefix}_${timestamp}`;
}
 
console.log(createId());       // "id_1738746000000" (different each call)
console.log(createId("user")); // "user_1738746000000"

Function Calls as Defaults

javascriptjavascript
function getDefaultName() {
  console.log("Computing default...");
  return "Guest";
}
 
function greet(name = getDefaultName()) {
  return `Hello, ${name}!`;
}
 
greet("Alice"); // "Hello, Alice!" (getDefaultName NOT called)
greet();        // "Computing default..." then "Hello, Guest!"

The default expression runs only when the default is actually needed. This is called lazy evaluation.

Referencing Previous Parameters

A default expression can reference parameters declared before it in the parameter list:

javascriptjavascript
function createElement(tag = "div", className = `${tag}-default`) {
  return `<${tag} class="${className}">`;
}
 
console.log(createElement());
// <div class="div-default">
 
console.log(createElement("section"));
// <section class="section-default">
 
console.log(createElement("nav", "main-nav"));
// <nav class="main-nav">
javascriptjavascript
function createRange(start = 0, end = start + 10) {
  const range = [];
  for (let i = start; i <= end; i++) {
    range.push(i);
  }
  return range;
}
 
console.log(createRange());     // [0, 1, 2, ..., 10]
console.log(createRange(5));    // [5, 6, 7, ..., 15]
console.log(createRange(5, 7)); // [5, 6, 7]
Forward References Do Not Work

A default cannot reference a parameter that comes after it. function f(a = b, b = 1) throws a [ReferenceError](/tutorials/programming-languages/javascript/basic-javascript-debugging-tips-for-beginners) when called as f() because b is not yet initialized when a tries to use it.

Destructured Parameters with Defaults

Default parameters combine naturally with destructuring:

Object Destructuring

javascriptjavascript
function createButton({ text = "Click", color = "blue", size = "medium" } = {}) {
  return `<button class="${color} ${size}">${text}</button>`;
}
 
console.log(createButton());
// <button class="blue medium">Click</button>
 
console.log(createButton({ text: "Submit", color: "green" }));
// <button class="green medium">Submit</button>

Notice the = {} after the destructuring pattern. This provides a default for the entire object, so calling createButton() with no arguments does not throw an error.

Array Destructuring

javascriptjavascript
function getCoordinates([x = 0, y = 0] = []) {
  return `(${x}, ${y})`;
}
 
console.log(getCoordinates());        // "(0, 0)"
console.log(getCoordinates([5]));     // "(5, 0)"
console.log(getCoordinates([3, 7]));  // "(3, 7)"

Practical Patterns

Configuration Objects

Functions that accept many options benefit from a destructured default object:

javascriptjavascript
function fetchData(url, options = {}) {
  const config = {
    method: "GET",
    timeout: 5000,
    retries: 3,
    cache: true,
    ...options,
  };
 
  console.log(`Fetching ${url} with:`, config);
  return config;
}
 
fetchData("/api/users");
// method: "GET", timeout: 5000, retries: 3, cache: true
 
fetchData("/api/users", { method: "POST", timeout: 10000 });
// method: "POST", timeout: 10000, retries: 3, cache: true

Required Parameter With a Helpful Error

javascriptjavascript
function required(paramName) {
  throw new Error(`Missing required parameter: ${paramName}`);
}
 
function createUser(name = required("name"), email = required("email")) {
  return { name, email, createdAt: new Date().toISOString() };
}
 
createUser("Alice", "alice@test.com"); // works
createUser("Alice"); // Error: Missing required parameter: email
createUser();        // Error: Missing required parameter: name

Feature Toggle with Defaults

javascriptjavascript
function initApp({
  darkMode = false,
  analytics = true,
  debugMode = false,
  language = "en",
  maxRetries = 3,
} = {}) {
  console.log("App initialized with:");
  console.log(`  Dark mode: ${darkMode}`);
  console.log(`  Analytics: ${analytics}`);
  console.log(`  Debug mode: ${debugMode}`);
  console.log(`  Language: ${language}`);
  console.log(`  Max retries: ${maxRetries}`);
}
 
initApp(); // all defaults
initApp({ darkMode: true, language: "es" }); // override two settings

Chained Defaults for Validation

javascriptjavascript
function clamp(value, min = 0, max = 100) {
  if (value < min) return min;
  if (value > max) return max;
  return value;
}
 
console.log(clamp(50));          // 50
console.log(clamp(150));         // 100
console.log(clamp(-10));         // 0
console.log(clamp(50, 10, 60));  // 50
console.log(clamp(5, 10, 60));   // 10

Common Mistakes

Mistake 1: Using null Instead of undefined

javascriptjavascript
function greet(name = "Guest") {
  return `Hello, ${name}!`;
}
 
// Passing null does NOT trigger the default
console.log(greet(null)); // "Hello, null!" (not "Hello, Guest!")

Mistake 2: Mutable Default Objects

javascriptjavascript
// WRONG: shared reference
function addItem(item, list = []) {
  list.push(item);
  return list;
}
 
// This is actually SAFE in JavaScript!
// Unlike Python, the default is evaluated fresh each call
console.log(addItem("a")); // ["a"]
console.log(addItem("b")); // ["b"] (new array each time)

Unlike Python, JavaScript evaluates default parameter expressions on each call. So = [] creates a new empty array every time the default is used.

Mistake 3: Skipping Middle Parameters

javascriptjavascript
function create(name = "Item", quantity = 1, price = 9.99) {
  return { name, quantity, price };
}
 
// WRONG: can't skip 'quantity' to set 'price'
// create("Widget", , 19.99); // SyntaxError
 
// Pass undefined to use the default
create("Widget", undefined, 19.99);
// { name: "Widget", quantity: 1, price: 19.99 }

If you frequently skip parameters, switch to an object parameter instead.

Rune AI

Rune AI

Key Insights

  • Only undefined triggers defaults: null, 0, "", and false are used as-is
  • Defaults are evaluated per call: unlike Python, = [] creates a fresh array each time
  • Put default parameters last: required parameters first avoids awkward undefined placeholders
  • Use object destructuring for many options: function f({ a = 1, b = 2 } = {}) scales well
  • Expressions and function calls work as defaults: they run only when the default is needed
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between default parameters and the OR operator for defaults?

Default parameters only trigger on `undefined`. The `||` operator triggers on all falsy values (`0`, `""`, `false`, `null`, `undefined`). The nullish coalescing operator `??` is closer to default parameters but works as an expression inside the function body rather than in the parameter list. Default parameters are the cleanest approach for function signatures.

Can I use async functions or await in default parameter expressions?

No. Default parameter expressions are evaluated synchronously. You cannot use `await` in a parameter default. If you need an async default, move the logic inside the function body: `async function f(data) { data = data ?? await fetchDefault(); }`.

Do default parameters work with arrow functions?

Yes. [Arrow function](/tutorials/programming-languages/javascript/javascript-arrow-functions-a-complete-es6-guide)s support default parameters with the same syntax: `const greet = (name = "Guest") => \`Hello, ${name}!\``. The behavior is identical to regular functions.

Is there a performance cost to using default parameters?

The cost is negligible. Default expressions are only evaluated when the argument is `undefined`, so there is no computation for arguments that are provided. Even [function call](/tutorials/programming-languages/javascript/how-to-declare-and-call-a-javascript-function)s as defaults (like `Date.now()`) are free when the argument is supplied.

Conclusion

Default parameters provide a clean, built-in way to handle missing function arguments in JavaScript. They trigger only when the argument is undefined, preserving intentional falsy values like 0, "", and false. Default expressions are evaluated lazily at call time, can reference earlier parameters, and combine naturally with destructuring for configuration objects.