Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
ES6 defaults / overrides pattern

ES6 Defaults / Overrides Pattern

Combine default parameters and destructuring for a compact version of the defaults / overrides pattern.

function foo ({
    bar = 'no',
    baz = 'works!'
  } = {}) {

  return (`${bar}, ${baz}`);
}

console.log(foo({
  bar: 'yay'
})); // logs 'yay, works!'

Equivalent to ES5:

This bit needs to be polyfilled, Or use $.extend(), _.extend(), lodash/object/assign aka _.assign() or equivalent.

var assign = Object.assign;

var defaults2 = {
    bar: 'no',
    baz: 'works!'
  };

function foo2 (options) {
  var settings = assign({}, defaults2, options),
    bar = settings.bar,
    baz = settings.baz;

  return (bar + ', ' +baz);
}

console.log(foo2({
  bar: 'yay'
})); // logs 'yay, works!'

Gist written for How to Use ES6 for Isomorphic JavaScript Apps

@nackjicholson

This comment has been minimized.

Copy link

commented Apr 20, 2015

@ericelliot at this es6-workshop at fluent, I just saw Axel Rauschmayer and Aaron Frost run this jest test.

it('it can default all arguments, optionally', () => {

      //Modify the method signature of `myFunction` to allow for all args to be optional

      function myFunction({name='Freddie', age=49, favoriteBand='Queen'} = {}){
        expect(name).toBeDefined();
        expect(age).toBeDefined();
        expect(favoriteBand).toBeDefined();
      }

      myFunction({ name: 'Axel', age: 37, favoriteBand: 'Taylor Swift' });
      myFunction({ name: 'Axel', age: 37 });
      myFunction({ name: 'Axel' });
      myFunction({});
      myFunction();

    });
  })

And have it pass, should I still be confused about how this works? Because I am.

@nackjicholson

This comment has been minimized.

Copy link

commented Apr 20, 2015

So that's assigning defaults on the left side of destructuring. Mind blown, very cool.

@ericelliott

This comment has been minimized.

Copy link
Owner Author

commented Apr 24, 2015

Updated.

@sethlivingston

This comment has been minimized.

Copy link

commented May 17, 2015

_.merge is preferred, isn't it?

https://blog.mariusschulz.com/2015/03/30/combining-settings-objects-with-lodash-assign-or-merge

And does the pitfall pointed out there affect your solution? I'm not sure because my mind is still blown. ;)

@ericelliott

This comment has been minimized.

Copy link
Owner Author

commented May 26, 2015

Whether or not to deep merge depends very much on the use-case. I'd say most of the time you don't want to merge. Once in a while, you do.

@koresar

This comment has been minimized.

Copy link

commented Jul 10, 2015

There's also _.defaults(options, defaults2)

@PhiLhoSoft

This comment has been minimized.

Copy link

commented Jul 21, 2015

Difference between Lodash's _.assign / _.extend, _.merge and _.defaults can be subtle.
I made a plunk to experiment with Lodash, here is a test:

var od = document.querySelector('#original-data');
var res = document.querySelector('#result');

var dest = { name: 'Foo', age: 25, driver: true  };
var src1 = { name: 'Bar', driver: false, address: 'Here and there'  };
var src2 = { name: 'Moo', maried: true, age: undefined };
var d1 = _.clone(dest);
var d2 = _.clone(dest);
od.innerText = JSON.stringify(dest) + '\n' + JSON.stringify(src1) + '\n' + JSON.stringify(src2);

res.innerText = 
  // assign overwrites all properties with successive values, including properties set to undefined
  JSON.stringify(_.assign(dest, src1, src2)) + '\n' + 
  // merge overwrites all properties with successive values, except undefined properties
  JSON.stringify(_.merge(d1, src1, src2)) + '\n' +
  // defaults assign properties which are undefined: no overwriting of exising properties
  JSON.stringify(_.defaults(d2, src1, src2));

Result:

Original Data

{"name":"Foo","age":25,"driver":true}
{"name":"Bar","driver":false,"address":"Here and there"}
{"name":"Moo","maried":true}

Result

{"name":"Moo","driver":false,"address":"Here and there","maried":true}
{"name":"Moo","age":25,"driver":false,"address":"Here and there","maried":true}
{"name":"Foo","age":25,"driver":true,"address":"Here and there","maried":true}
@PhiLhoSoft

This comment has been minimized.

Copy link

commented Jul 21, 2015

With deep copy: http://plnkr.co/edit/M7ZlnxLrNhDGyGNuq7eh

var dest = { name: 'Foo', age: 25, driver: true, 
    id: { driverLicense: '88-65', ssn: '1-54-455', other: 'mango' }  };
var src1 = { name: 'Bar', driver: false, address: 'Here and there', 
    id: { driverLicense: undefined, ssn: '2-44-985', pwd: '123' }  };
var src2 = { name: 'Moo', maried: true, age: undefined, address: 'Somewhere', 
    id: { driverLicense: '11-75', ssn: undefined, warcry: 'Kowabunga' } };

var d1 = _.cloneDeep(dest);
var d2 = _.cloneDeep(dest);
var d3 = _.cloneDeep(dest);

od.innerText = JSON.stringify(dest) + '\n' + JSON.stringify(src1) + '\n' + JSON.stringify(src2);

res.innerText = 
  // assign overwrites all properties with successive values, including properties set to undefined
  JSON.stringify(_.assign(dest, src1, src2)) + '\n' + 
  // merge overwrites deeply all properties with successive values, except undefined properties
  JSON.stringify(_.merge(d1, src1, src2)) + '\n' +
  // defaults assigns properties which are undefined: no overwriting of exising properties
  JSON.stringify(_.defaults(d2, src1, src2)) + '\n' +
  // defaultsDeep assigns deeply properties which are undefined: no overwriting of exising properties
  JSON.stringify(_.defaultsDeep(d3, src1, src2));

Result:

Original Data

{"name":"Foo","age":25,"driver":true,
    "id":{"driverLicense":"88-65","ssn":"1-54-455","other":"mango"}}
{"name":"Bar","driver":false,"address":"Here and there",
    "id":{"ssn":"2-44-985","pwd":"123"}}
{"name":"Moo","maried":true,"address":"Somewhere",
    "id":{"driverLicense":"11-75","warcry":"Kowabunga"}}

Result

{"name":"Moo","driver":false,
    "id":{"driverLicense":"11-75","warcry":"Kowabunga"},
    "address":"Somewhere","maried":true}
{"name":"Moo","age":25,"driver":false,
    "id":{"driverLicense":"11-75","ssn":"2-44-985","other":"mango","pwd":"123","warcry":"Kowabunga"},
    "address":"Somewhere","maried":true}
{"name":"Foo","age":25,"driver":true,
    "id":{"driverLicense":"88-65","ssn":"1-54-455","other":"mango"},
    "address":"Here and there","maried":true}
{"name":"Foo","age":25,"driver":true,
    "id":{"driverLicense":"88-65","ssn":"1-54-455","other":"mango","pwd":"123","warcry":"Kowabunga"},
    "address":"Here and there","maried":true}
@ericelliott

This comment has been minimized.

Copy link
Owner Author

commented Jul 22, 2015

👍

@vitalets

This comment has been minimized.

Copy link

commented Oct 19, 2017

Thanks for the comparison!
For me behavior of _.defaults is most preferable, because it allows to define conditional options with ternary operator that does not overwrite default values:

var src2 = { 
  age: someCondition ? age : undefined 
};
_.defaults(src2, {age: 25}); // {age: 25}

Also for one-level options I've made flat-options package that additionally performs validation of keys and has twice smaller size than _.defaults.

@marcelo-ribeiro

This comment has been minimized.

Copy link

commented Mar 2, 2018

👍

@mathieug

This comment has been minimized.

Copy link

commented Mar 5, 2018

Nice trick @ericelliott! How can we name the object parameter if we need an object with all the parameters (given and default)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.