Optional Arguments in JavaScript Functions

Dec 22, 2015

When writing our JavaScript code, sometimes it can be useful to be able to provide a function with optional arguments. If you're coming from a language like Python this is quite simple.

>>> def formalName(lastname, title="Mr"):
...     print(title + ". " + lastname)
>>> formalName("Webb")
Mr. Webb

* Running in the Python interpreter *

Some might think they can just do the same thing in JavaScript. Well I'm sorry to tell you, you can't. Before you get too down, there is a way to do it which is just as easy. Let's write out the same function in JavaScript:

function formalName(lastname, title){
  var title = title || 'Mr';
  console.log(title + '. ' + lastname);
}
formalName('Webb'); //=> Mr. Webb

As you can see, running this function works exactly the same way it would in Python. For small use cases this is fine. If your function only takes in 1 or 2 arguments, this isn't a big deal. My big problem with this method is that you have to put all of your defaults at the end of the argument list. And when you have multiple default arguments, this can start to become a problem. Let's say for instance you have a function that takes 5 arguments. The last 3 arguments have default values. What if you want to use the default value for the 3rd argument, but you want custom values for the 4th and 5th? You can't do that. How would you even go about omitting the 3rd value? Let's rewrite our previous function to test this out:

function formalName(title, lastname){
  var title = title || 'Mr';
  console.log(title + '. ' + lastname);
}

formalName( , 'Webb'); //=> Uncaught SyntaxError: Unexpected token ,(…)

That doesn't work. Like I said earlier, in something small and simple like this you can just swap the arguments around and you'll be fine. It's not so easy when you have a much larger application and you want multiple functions to have multiple arguments with default values. In these instances you're better off using an object for your argument (or multiple objects depending on how you separate things). This makes the default value issue much easier to manage. Using an object prevents you from having to worry about the order of your arguments, and it also looks much cleaner when your function is taking in 10+ arguments (if you really need that many arguments). Check out this example:

function animalSounds(animals){
  var animals = clone(animals); // Feel free to skip this line and just use the passed in animals variable
  animals.dog = animals.dog || 'Bark';
  animals.cat = animals.cat || 'Meow';
  var str = '';
  str += "Dogs like to " + animals.dog;
  str += "\nCats like to " + animals.cat;
  str += "\nPigs like to " + animals.pig;
  console.log(str);
}



// The following function is for cloning objects since
// objects are passed in by reference. Use this
// so you're not overwriting the object that
// was passed into the function.
// This is not particularly important for the
// example and can be ignored.
function clone(obj){
  var copy = obj.constructor();
    for (var attr in obj) {
      if (obj.hasOwnProperty(attr)){ 
        copy[attr] = obj[attr];
      }
    }
  return copy;
}

When using animalSounds(), you pass in an object with the sounds of a dog, cat, and a pig. The only required property (for this to work as intended) is the pig property. You can omit the other two and there will be no issues. Here are some examples:

> animalSounds({pig: 'Oink'});
  Dogs like to Bark
  Cats like to Meow
  Pigs like to Oink
  undefined
> animalSounds({pig: 'Oink', dog: 'Ruff'});
  Dogs like to Ruff
  Cats like to Meow
  Pigs like to Oink
  undefined

* Running in the JavaScript console *

As you can see in the first example everything works correctly when only providing the pig property and in the second example the dog property properly changes the sound dogs make. I mentioned earlier that this method of placing arguments makes for better formatting. Let me show you what I mean. Let's assume we've added code to deal with more animals in animalSounds(). This is how you would format your call to the function:

animalSounds({
  pig: 'Oink',
  dog: 'Ruff',
  duck: 'Quack',
  monkey: 'Ooh Ooh Aah Aah',
  seal: 'Arf',
  mouse: 'Squeak'
});

Even without using default values, that makes much more sense to anyone reading it. Much better than:

animalSounds('Oink', 'Ruff', 'Quack', 'Ooh Ooh Aah Aah', 'Arf', 'Squeak');

This pattern isn't something I made up. This is used by tons of JavaScript developers and is an extremely useful pattern to implement in your code. Try this out and let me know how it works for you.

*Update July 21, 2016*

In ECMAScript 6, functionality is being added for default parameters. Check out these examples:

ECMAScript 5:

function log(param) {
  if (param === undefined) {
    param = 'Hello'
  }
 
  console.log(param)
}

log() //=> Hello
log('World') //=> World

ECMAScript 6:

function log(param = 'Hello'){
  console.log(param)
}

log() //=> Hello
log('World') //=> World

For a deeper look at default paramaters, check out the article by Smashing Magazine that discusses this. It also talks about the new spread operator and REST parameters. Great read and I highly recommend it.