Default Parameters in JavaScript
designpepper.com
designpepper.com
function foo(opts){
var defaults = {bar: "baz", foo: "biff"};
_.defaults(opts, defaults);
// do things with opts here...
}
The difference between _.defaults and _.extend is significant; _.extend will overwrite all properties in the first object with those defined in the second (and third, fourth, etc.), while _.defaults will only overwrite properties that are null or undefined.The advantage is, using _.defaults, that you aren't passing a defaults object around within the body of your method and using it as if it were the opts, or reassigning to yet another var (see `finalParams` defined in the last example). This makes the method a bit clearer.
var myopts = {foo: 1}
foo(myopts)
console.log(myopts) // => {foo: 1, bar: "baz"} var actual_opts = _.defaults({}, opts, {
// Your defaults here
});
where ``opts`` is the options object passed in.This also fixes the issue of a caller passing in no options (or "null" or "undefined") without having to add an `||`
Unless you have a truly huge set of options, I'd suggest using
var final_opts = _.defaults({}, opts, defaults);
instead: `opts` being `null` or `undefined` will be equivalent to an empty object.How and why? I don't consider mutating the original object to be an advantage of using _.defaults, unless the object is completely under your control and you're essentially calling `_.defaults(this, defaults)`.
function(arg1, arg2) {
arg1 = arg1 || "foo";
arg2 = arg2 || "bar";
}
it will fail of course if you expect to accept falsy args tho, then you need a bit moar kludge... arg1 = arg1 !== 0 ? 1234 : arg1;
arg1 = arg1 !== false ? "foo" : arg1;
arg1 = arg1 !== null ? "foo" : arg1;
arg1 = arg1 !== "" ? "foo" : arg1;
and if you expect to accept multiple types of falsy values for any one argument (which requires a lot more kludge), you should probably change your API :)A slightly more verbose, yet hardy syntax would go like this:
function foo(arg1, arg2){
arg1 = arg1 === undefined ? "foo" : arg1;
arg2 = arg2 === undefined ? "bar" : arg2;
}
This would catch the intended behavior, which is to fill a parameter when it is not supplied at all.In fact you're just compacting an if:
if (arg1 == null) { // check for both `undefined` and `null`
arg1 = 'default';
}
// compacted to:
arg1 = arg1 == null ? 'default' : arg1;
The difference is that you're always doing an assignement, even if unnecessary. (arg1 == null) && (arg1 = 'default');I could see, using the if statement form, that you might have a programmer decide to compact it and drop the brackets - and then you might fall in the trap where somebody thinks they're in an if block when they're really not. Happens pretty often.
In addition, I don't know if you really would want to check for null. I usually just check for undefined, which would be the absence of any provided value (as opposed to a provided value that just happens to be null). Of course, it depends on the app.
In addition, I really don't like using short-circuited boolean ops for assignment. The style is grating to me - it's not immediately obvious what it's really doing. To each his own.
EDIT: Created a simple jsperf test. Difference is about 6.8%, but we're talking hundreds of millions of ops/s. Not the kind of stuff that matters outside of raw benches.
myfn('a',undefined,'b'); JSON.parse(object, undefined, 2); var foo = function(a, b) {
if (arguments.length < 2) {
b = 'some default value';
}
// ...
}; function(kwargs) {
kwargs = _.extend({param1: true, param2: 42}, kwargs)
// ...
} var args = _.defaults({}, kwargs, {param1: true, param2: 42}).