NeuroAgent

JavaScript Default Parameters: Complete Guide

Learn how to implement default parameter values in JavaScript functions using ES6 syntax. This complete guide covers behavior with different input types, advanced techniques, and browser compatibility.

Question

How to set default parameter values in JavaScript functions?

I want to create a JavaScript function with optional arguments that have default values. These defaults should be used when the argument is not provided, but ignored when a value is explicitly passed.

In Ruby, this can be achieved with syntax like:

ruby
def read_file(file, delete_after = false)
  # code
end

Does JavaScript support similar default parameter syntax? For example, would this work:

javascript
function read_file(file, delete_after = false) {
  // Code
}

What is the correct way to implement default parameters in JavaScript functions?

NeuroAgent

JavaScript supports default parameter values through ES6 syntax that closely resembles Ruby’s approach. You can set default values directly in the function parameter list, making your code more concise and readable than traditional conditional checks. This feature has been widely supported in modern browsers and JavaScript environments since its introduction.

Contents

Understanding JavaScript Default Parameters

Default parameters allow you to initialize function parameters with default values if no value or undefined is passed. This eliminates the need for manual checks and provides a cleaner syntax for handling optional arguments.

Unlike Ruby which uses the equals sign (=) for default values, JavaScript also uses the equals sign but within the function parameter declaration. This creates a straightforward and intuitive way to define optional parameters.

The key difference from Ruby is that JavaScript’s default parameters only apply when the argument is undefined, not when it’s any other falsy value like null, 0, false, or "". This behavior is important to understand when implementing default parameters.

ES6 Default Parameter Syntax

The ES6 syntax for default parameters is exactly what you suggested in your question. You can assign default values directly in the function parameter list:

javascript
function read_file(file, delete_after = false) {
  console.log(`Reading file: ${file}`);
  console.log(`Delete after: ${delete_after}`);
}

This syntax works perfectly in modern JavaScript environments. Here are more examples:

javascript
// Basic default parameter
function greet(name = "Guest") {
  return `Hello, ${name}!`;
}

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

// Multiple default parameters
function create_user(name, age = 25, role = "user") {
  return { name, age, role };
}

console.log(create_user("John")); 
// { name: "John", age: 25, role: "user" }

console.log(create_user("Jane", 30, "admin")); 
// { name: "Jane", age: 30, role: "admin" }

The default values are evaluated at call time, not at function definition time. This means you can even use expressions or function calls as default values:

javascript
function get_timestamp(date = new Date()) {
  return date.getTime();
}

console.log(get_timestamp()); // Current timestamp

Behavior with Different Input Types

JavaScript’s default parameters have specific behavior when different types of values are passed:

Undefined vs Other Falsy Values

Default parameters only trigger when the argument is undefined. Other falsy values like null, 0, false, or "" are treated as valid arguments:

javascript
function test(value = "default") {
  return value;
}

console.log(test(undefined)); // "default"
console.log(test(null));      // null
console.log(test(0));         // 0
console.log(test(false));     // false
console.log(test(""));        // ""

This behavior is different from Ruby’s default parameters, which would use the default for any falsy value.

Default Parameters with Objects and Arrays

Default parameters work seamlessly with objects and arrays:

javascript
function process_data(data = {}, options = []) {
  return { ...data, processed: true, options };
}

console.log(process_data());
// { processed: true, options: [] }

console.log(process_data({ id: 1 }, ["option1", "option2"]));
// { id: 1, processed: true, options: ["option1", "option2"] }

Default Parameters with Rest Parameters

You can combine default parameters with rest parameters:

javascript
function process_items(...items) {
  const processed = items.map(item => item * 2);
  return processed;
}

function calculate_total(items, tax = 0.1) {
  const subtotal = items.reduce((sum, item) => sum + item, 0);
  return subtotal * (1 + tax);
}

console.log(calculate_total([10, 20, 30]));      // 66 (60 + 10% tax)
console.log(calculate_total([10, 20, 30], 0.2)); // 72 (60 + 20% tax)

Advanced Default Parameter Techniques

Using Function Calls as Defaults

You can use function calls as default values, which are executed only when needed:

javascript
function get_user_data(id = fetch_user_id()) {
  return `User data for ID: ${id}`;
}

function fetch_user_id() {
  console.log("Fetching user ID...");
  return 123;
}

console.log(get_user_data()); // Logs "Fetching user ID..." then returns "User data for ID: 123"
console.log(get_user_data(456)); // Returns "User data for ID: 456" (no function call)

Default Parameters with Destructuring

Default parameters work beautifully with destructuring:

javascript
function format_user({ name = "Anonymous", age = 18, role = "user" } = {}) {
  return { name, age, role };
}

console.log(format_user()); // { name: "Anonymous", age: 18, role: "user" }
console.log(format_user({ name: "Alice" })); // { name: "Alice", age: 18, role: "user" }

Parameter Evaluation Order

Default parameters are evaluated from left to right, and later parameters can use earlier ones:

javascript
function calculate(a = 1, b = a * 2, c = a + b) {
  return { a, b, c };
}

console.log(calculate());      // { a: 1, b: 2, c: 3 }
console.log(calculate(5));     // { a: 5, b: 10, c: 15 }
console.log(calculate(2, 8));  // { a: 2, b: 8, c: 10 }

Comparison with Traditional Methods

Before ES6, developers used several approaches to handle default parameters:

Conditional Check Method

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

Issues: This treats all falsy values the same way, including 0, false, and "".

Explicit Check for Undefined

javascript
function old_greet(name) {
  if (name === undefined) {
    name = "Guest";
  }
  return `Hello, ${name}!`;
}

Ternary Operator

javascript
function old_greet(name) {
  const finalName = typeof name !== 'undefined' ? name : "Guest";
  return `Hello, ${finalName}!`;
}

Arguments Object Usage

javascript
function old_greet() {
  const name = arguments.length > 0 ? arguments[0] : "Guest";
  return `Hello, ${name}!`;
}

Comparison:

Method Readability Precision ES6+ Performance
ES6 Default Parameters Excellent High (only undefined) Best
Conditional Check (` `) Good Low (all falsy)
Explicit Check Fair High (only undefined) Fair
Ternary Operator Fair High (only undefined) Fair
Arguments Object Poor Low Poor

The ES6 default parameters approach is clearly superior in terms of readability, precision, and maintainability.

Browser Compatibility and Polyfills

Native Support

Default parameters are widely supported in all modern browsers and JavaScript environments:

  • Chrome: 49+ (March 2016)
  • Firefox: 36+ (February 2015)
  • Safari: 10+ (September 2016)
  • Edge: 14+ (August 2016)
  • Node.js: 6+ (April 2016)

Polyfill for Older Environments

If you need to support older browsers, you can use this polyfill:

javascript
// From MDN polyfill
(function() {
  'use strict';
  
  // Check for existing implementation
  if (!Function.prototype.bind) {
    Function.prototype.bind = function(oThis) {
      if (typeof this !== 'function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
      }
      
      var aArgs = Array.prototype.slice.call(arguments, 1),
          fToBind = this,
          fNOP = function() {},
          fBound = function() {
            return fToBind.apply(this instanceof fNOP && oThis
                 ? this
                 : oThis,
                 aArgs.concat(Array.prototype.slice.call(arguments)));
          };
      
      fNOP.prototype = this.prototype;
      fBound.prototype = new fNOP();
      
      return fBound;
    };
  }
})();

Transpilation with Babel

For projects that need to support older environments, using Babel to transpile ES6 code to ES5 is the recommended approach:

bash
npm install --save-dev @babel/core @babel/preset-env

Then create a .babelrc file:

json
{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "browsers": ["> 1%", "last 2 versions"]
      }
    }]
  ]
}

This will automatically transpile default parameters to compatible code for your target browsers.


Sources

  1. MDN Web Docs - Default parameters
  2. ECMAScript 2015 Language Specification - Function Definitions
  3. JavaScript.info - Default parameters
  4. 2ality - ES6 default parameters
  5. Babel - Default parameters

Conclusion

JavaScript’s ES6 default parameter syntax provides a clean and efficient way to handle optional function arguments. The syntax you proposed works perfectly and is the recommended approach in modern JavaScript development.

Key takeaways:

  • Use the parameter = defaultValue syntax directly in function declarations
  • Default values only apply when arguments are undefined, not other falsy values
  • Default parameters are evaluated at call time, allowing for dynamic defaults
  • The approach is more readable and maintainable than traditional conditional checks
  • Combine default parameters with destructuring for powerful, clean code

For maximum compatibility, consider using transpilation tools like Babel in projects that need to support older browsers. However, for modern web development, native default parameter support is robust and reliable across all major browsers.