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.