111

I constantly find myself passing config values to functions accessing them like this:

var arg1 = 'test1';
if(isUndefined(config.args.arg1)){
  arg1 = config.args.arg1;
} 

var arg2 = 'param2';
if(isUndefined(config.args.arg2)){
  arg2 = config.args.arg2;
} 

var arg3 = '123';
if(isUndefined(config.args.arg3)){
  arg3 = config.args.arg3;
} 

where I later use them like this:

var url = '<some-url>?id='+arg1+'&='+arg2 +'=' + arg3;

Does jQuery/ExtJS or any other framework provide a solution to access variables like this in a simple way, and give variables a default value?

Something like:

getValueOfObject(config,'args.arg3','<default>');

Or is there maybe a standard solution for this.

NOTE:

I was also thinking about the common pattern where you have defaults

var defaults = {
   args: {
      args1: ....
   }
   ...
}

and doing an object merge.

And then encoding the object to a param String. But as you can see the object values also sometimes contain parameter names.

Martin Serrano
  • 3,727
  • 1
  • 35
  • 48
Jeremy S.
  • 6,423
  • 13
  • 48
  • 67

5 Answers5

110

Generally, one can use the or operator to assign a default when some variable evaluates to falsy:

var foo = couldBeUndefined || "some default";

so:

var arg1 = config.args.arg1 || "test";
var arg2 = config.args.arg2 || "param2";

assuming that config.args is always defined, as your example code implies.

karim79
  • 339,989
  • 67
  • 413
  • 406
  • 6
    I believe it will be ok if `config.args.arg1` is undefined, but wont this fail if `config` or `config.args` is itself undefined? – James Wiseman Jan 25 '12 at 14:14
  • Nope, undefined evaluates to falsy, so the second part of the condition gets assigned to `foo` in such a scenario. – karim79 Jan 25 '12 at 14:15
  • 4
    @karim79 he's implying `config.args.arg1` will throw if `config.args === undefined` – Raynos Jan 25 '12 at 14:16
  • Well yeah, it won't handle those scenarios, so `config` and `config.args` would need to be checked beforehand. – karim79 Jan 25 '12 at 14:16
  • Thanks @raynos, that's exactly what I was trying to imply :-) – James Wiseman Jan 25 '12 at 14:19
  • @James and @Karim that's exactly what I meant, either `config` or `config.args` could be undefined in my case which would result in a exception thrown ... therefore a function that parses the object graph for a value, in hindsight that a path could be `undefined` – Jeremy S. Jan 25 '12 at 17:32
  • 41
    There's a big flaw here. If `couldBeUndefined = 0`, or any other falsy value, then `foo = "some default"`, instead of getting the desired `foo = 0` – Daniel Reina Jun 16 '17 at 00:27
  • 7
    This will fail, if the value is false. You should do `typeof x.y != 'undefined' ? x.y : "";` – gwthm.in Aug 02 '17 at 18:45
  • 4
    when arg is boolean and default value is true, everything ruined. – KIDJourney Aug 07 '17 at 02:10
  • I used to use this. It recently caused a bug because one of my values was `-0`, took a while to figure out what was causing the bug. It will also fail for empty strings and other stuff which could be valid if unusual data. – AnnanFay Jul 12 '18 at 12:29
  • @karim79 Please edit your answer as it is plainly flawed and will cause bugs in peoples' code as has already been point out by Daniel Reina. Leaving it like it is, is irresponsible. – Elmar Zander Nov 05 '19 at 14:17
  • 4
    Note that in more recent JS, you could use `??` over `||` which would match only nullish values, rather than falsey which might address some of the above concerns. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator – Mark Jun 19 '20 at 09:15
37

With ECMAScript 2020 it may be safer to use Nullish coalescing operator than or operator.

let foo = obj.maybeUndefined ?? "default value";

JS treats 0, -0, null, false, NaN, undefined, or the empty string ("") as falsy. So, using or operator for setting a default value in the following example may cause unexpected results

let obj = { maybeUndefined: 0 };
let foo = obj.maybeUndefined || 1; // foo == 1

Here, foo becomes equal to 1, when maybeUndefined is actually defined. If foo should be assigned a default value only when maybeUndefined is actually undefined or null, then we can use Nullish coalescing operator.

let obj = { maybeUndefined: 0 };
let foo = obj.maybeUndefined ?? 1; // foo == 0

let obj = {};
let foo = obj.maybeUndefined ?? 1; // foo == 1
Timur Osadchiy
  • 5,699
  • 2
  • 26
  • 28
24

Looks like finally lodash has the _.get() function for this!

Jeremy S.
  • 6,423
  • 13
  • 48
  • 67
13

With ES2018, you can now write options = { ...defaults, ...options }:

Spread syntax - JavaScript | MDN

Shallow-cloning (excluding prototype) or merging of objects is now possible using a shorter syntax than Object.assign().

const obj1 = { foo: 'bar', x: 42 };
const obj2 = { foo: 'baz', y: 13 };

const clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }

const mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }
Clément
  • 12,299
  • 15
  • 75
  • 115
3

try var options = extend(defaults, userOptions);

This way you get all the userOptions and fall back to defaults when they don't pass any options.

Note use any extend implementation you want.

Raynos
  • 166,823
  • 56
  • 351
  • 396
  • 4
    `extend` usually modifies and returns its first argument. This means you usually want `extend({}, defaults, userOptions)`, so that defaults is not changed. – Michael Anderson May 23 '13 at 08:41
  • 2
    Your version of extend may well be immutable, but all the other versions I've come across including the versions from jQuery (http://api.jquery.com/jQuery.extend/), Lodash and underscore(http://underscorejs.org/#extend) all use the non-immutable form. – Michael Anderson May 27 '13 at 00:37
  • Note: there is also the shortcut `_.defaults` for exactly this purpose. – sk29910 Oct 24 '15 at 18:47