Default options - avoid boolean OR

A common pattern in Javascript is the use of options objects to pass multiple parameters to a function. There's a particular pitfall in how people often implement this useful pattern. If you're not careful, passing in false, null, empty strings (and a few others) becomes impossible - making your code operate on the wrong parameters!

An options object is a plain Javascript object, whose keys are the parameters of a function you wish to call. You usually want some (or all) of these arguments to be optional. There are many examples around the internet of using the || operator to make this work.

For example:

function moveCar(options) {
  var speed = options.speed || 12;
  var weight = options.weight || 12000;
  // ... update physics calculations
}

moveCar({ weight: 2e4 });

Here, speed will assume the value of 12, since the object passed into moveCar() didn't have a speed key. This works in most situations, but fails in a dangerous way if you want to set a parameter to one of 0, false, "", null, undefined, or NaN. Let's modify the example a little.

function moveCar(options) {
  var speed = options.speed || 12;
  var weight = options.weight || 12000;
  var ac = options.hasAirConditioning || true;
  // ... update physics calculations
}

moveCar({ speed: 0, hasAirConditioning: false });

Now when we call moveCar(), the car's speed will be given a value of 12, and its air conditioning flag will be set to true. This is because || expects boolean arguments on its left and right sides. When given values that aren't booleans, || implicitly converts them, and returns the first true value (reading left to right). 0, "", null, undefined, and NaN all convert to the value false. Back in the example, var speed = options.speed || 12 results in a value of 12, since options.speed has a value of 0 (converts to false). As you can see, || is convenient but carries some caveats.

A better solution is to use jQuery's $.extend() function. Modifying our example:

function moveCar(options) {
  var defaults = {
    speed: 12,
    weight: 12000,
    hasAirConditioning: false,
  };
  var opts = $.extend({}, defaults, options);
  var speed = opts.speed;
  var weight = opts.weight;
  var ac = opts.hasAirConditioning;
  // ... update physics calculations
}

moveCar({ speed: 0, hasAirConditioning: false });

The code now works as expected: the car's speed is set to 0, and its air conditioning flag is set to false.

© 2021 David Mann. All content provided on this site, code or otherwise, is licensed and/or copyright as specified below: