javascript - Destructuring objects as function parameters deep extend - Stack Overflow

Is it possible to "deep extend" objects when using destructuring to set default properties of

Is it possible to "deep extend" objects when using destructuring to set default properties of an object passed into a function?

Example:

function foo({
  foo = 'foo',
  bar = 'bar',
  baz = {
    propA: 'propA',
    propB: 'propB'
  }
} = {}) {
  console.log(foo);
  console.log(bar);
  console.log(baz);
}

foo({
  foo: 'changed',
  baz: {
    propA: 'changed'
  }
});

Is it possible to "deep extend" objects when using destructuring to set default properties of an object passed into a function?

Example:

function foo({
  foo = 'foo',
  bar = 'bar',
  baz = {
    propA: 'propA',
    propB: 'propB'
  }
} = {}) {
  console.log(foo);
  console.log(bar);
  console.log(baz);
}

foo({
  foo: 'changed',
  baz: {
    propA: 'changed'
  }
});

This outputs: (baz is overwrote)

changed
bar
{
  "propA": "changed"
}

Is there syntax which will extend the baz object, to give output:

changed
bar
{
  "propA": "changed",
  "propB": "propB"
}
Share Improve this question asked Dec 5, 2016 at 11:42 FergalFergal 2,4744 gold badges36 silver badges49 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 6

There is no native-way to do what you ask, but you can write your own function decorator, of course it is a verbose approach...

Note: In order to perform a deep merge you should use some of the existing solution developed by the munity, both Object.assign and Object Spread Operator perform a shallow copy of the source, even, writing your own deepmerge function could be error-prone. I suggest you to use lodash#merge that is monly accepted as one of the solid solutions to the problem.

var {
  // https://lodash./docs/#merge
  merge
} = require('lodash');

function defaultsArgDecorator(defaults, target) {

  return function(args) {
    
    return target(
      merge(Object.create(defaults), args)
    )
  }
}


function _foo(args) { console.log({args}); }
var foo = defaultsArgDecorator({
  foo: 'foo',
  bar: 'bar',
  baz: {
    propA: 'propA',
    propB: 'propB'
  }
}, _foo);

foo({bar: "ciao bello", baz: {propA: "HELLO"}})

Since parameters destructuring allow you to use previous parameters in the defaults of other parameters, you can create a property that doesn't exit on the original parameters, for example __baz, and set its defaults using baz. In the method you'll use __baz instead of baz.

Note: this is a hack, and if the object contains a property by the name of __baz it will override the default, with unexpected results. However, you can name the default property with something like dontUse__baz, which has a very low chance of being used.

Default properties using Object#assign:

function foo({
  foo = 'foo',
    bar = 'bar',
    baz,
    __baz = Object.assign({  
      "propA": "changed",
      "propB": "propB"
    }, baz)
} = {}) {
  console.log(foo);
  console.log(bar);
  console.log(__baz);
}

foo({
  foo: 'changed',
  baz: {
    propA: 'changed'
  }
});


Default properties using object spread (requires babel plugin - see link):

function foo({
  foo = 'foo',
    bar = 'bar',
    baz,
    __baz = {  
      "propA": "changed",
      "propB": "propB",
      ...baz
    }
} = {}) {
  console.log(foo);
  console.log(bar);
  console.log(__baz);
}

foo({
  foo: 'changed',
  baz: {
    propA: 'changed'
  }
});

You can also do nested destructuring. You destructure baz, but also destructure propA and propB.

If you need your object whole and don't want to 'rebuild' it, this isn't for you, but it does acplish one objective of setting default values for unspecified properties nested in an object.

I've left baz as a separate descructured value in addition to it's properties as an example of a sometimes handy pattern, but you would most likely want to remove it since it suffers from the same problem of losing default properties.

function foo({
  foo = 'foo',
  bar = 'bar', 
  baz,  // likely extraneous and confusing, here for example
  baz: { propA = 'propA', propB = 'propB' }
} = {}) {
  console.log(foo);
  console.log(bar);
  console.log(propA, propB);
  console.log(baz);
  console.log(Object.assign(baz, {propA, propB}));
}

foo({
  foo: 'changed',
  baz: {
    propA: 'changed'
  }
});

Yep, totally possible!

Solution 1:

function foo({
  foo = 'foo',
  bar = 'bar',
  baz: {
    propA = 'propA',
    propB = 'propB'
  } = {}
}) {
  console.log(foo)
  console.log(bar)
  console.log({propA, propB}) // Because `bar` is renamed to `{propA, propB}`. Hence, no local variable named `baz`
}

Solution 2 (Workaround):

function foo({
  foo = 'foo',
  bar = 'bar',
  baz = {}
}) {
  // ...
  const baz2 = Object.assign({}, {propA: 'propA', propB: 'propB'} = {})
  console.log(baz2) // Yeah, not really a solution
}

You can even do this:

const func = ([{foo, bar: {a, b: BB, c: CC = 'c'} = {}} = {}], ...{0: rest0 = 0, 1: rest1 = '1'}) => { /* do stuffs */ }

The code above is a plicated example, I'll use simpler examples to explain:

Example 1:

const f1 = ({x: varx}) => console.log(varx)

When you call f1(obj), obj.x will be assigned to varx in f1's scope

Example 2:

const f2 = ({xy: {x, y}}) => console.log({x, y})

Similar to f1 but with {x, y} in place of varx, obj.xy will be assigned to {x, y}, so x = obj.xy.x and y = obj.xy.y

Example 3: Now let add create default parameters

const f3 = ({xy: {x, y} = {}}) => { /* do stuffs */ }

Now, you still have to pass obj as an object but you no longer have to provide obj.xy. If obj.xy is undefined, x and y are undefineds

Example 4: Now, let x and y have default values

const f4 = ({xy: {x = 'XXX', y = 'YYY'} = {}}) => { /* do stuffs */ }

If obj.xy you passed is undefined, {x, y} will be set to {x: undefined, y: undefined}, but since x and y also have default values, x will be 'XXX' and y will be 'YYY'

Example 4: "THE ULTIMATE LIFE FORM"

const f5 = ({xy: {x = 'XXX', y = 'YYY'} = {}} = {}) => { /* do stuffs */ }

Guess I don't have to explain this anymore :D

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745421389a4626978.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信