将对象作为函数参数进行深度扩展

Fer*_*gal 6 javascript ecmascript-6

在使用解构来设置传递给函数的对象的默认属性时,是否可以"深度扩展"对象?

例:

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'
  }
});
Run Code Online (Sandbox Code Playgroud)

这输出:(巴兹被覆盖)

changed
bar
{
  "propA": "changed"
}
Run Code Online (Sandbox Code Playgroud)

是否存在扩展baz对象的语法,以提供输出:

changed
bar
{
  "propA": "changed",
  "propB": "propB"
}
Run Code Online (Sandbox Code Playgroud)

Hit*_*nds 5

没有本地方法可以做你所要求的,但你可以自己编写function decorator,当然这是一个冗长的方法......

注意:为了执行深度合并,您应该使用社区开发的一些现有解决方案,Object.assignObject Spread Operator执行源的浅层副本,即使编写自己的deepmerge函数也可能容易出错.我建议你使用lodash#merge这是常见的最佳解决方案.

var {
  // https://lodash.com/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"}})
Run Code Online (Sandbox Code Playgroud)


Ori*_*ori 5

由于参数解构允许您在其他参数的默认值中使用以前的参数,因此您可以创建一个不在原始参数上存在的属性,例如__baz,并使用 设置其默认值baz。在该方法中,您将使用__baz而不是baz.

注意:这是一个 hack,如果对象包含一个名称为属性的属性,__baz则会覆盖默认值,从而产生意外的结果。但是,您可以使用诸如 之类的名称来命名默认属性 dontUse__baz,但使用该属性的可能性非常低。

默认属性使用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'
  }
});
Run Code Online (Sandbox Code Playgroud)


使用对象传播的默认属性(需要 babel 插件 -请参阅链接):

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'
  }
});
Run Code Online (Sandbox Code Playgroud)


DMa*_*ter -3

是的,完全有可能!

解决方案一:

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`
}
Run Code Online (Sandbox Code Playgroud)

解决方案 2(解决方法):

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

你甚至可以这样做:

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

上面的代码是一个复杂的例子,我将用更简单的例子来解释:

示例1:

const f1 = ({x: varx}) => console.log(varx)
Run Code Online (Sandbox Code Playgroud)

当你调用时f1(obj)obj.x将被分配到varxinf1的范围内

示例2:

const f2 = ({xy: {x, y}}) => console.log({x, y})
Run Code Online (Sandbox Code Playgroud)

与 类似,f1但用{x, y}代替varx,obj.xy将被分配给{x, y}, sox = obj.xy.xy = obj.xy.y

示例 3:现在让 add 创建默认参数

const f3 = ({xy: {x, y} = {}}) => { /* do stuffs */ }
Run Code Online (Sandbox Code Playgroud)

现在,您仍然必须obj作为对象传递,但不再需要提供obj.xy. 如果obj.xyundefinedx并且yundefineds

示例 4:现在,让xy具有默认值

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

如果obj.xy您传递了 is undefined{x, y}则将被设置为{x: undefined, y: undefined},但由于xy也有默认值,因此xwill be'XXX'ywill be'YYY'

示例 4:“终极生命形式”

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

我想我不必再解释了:D

  • @DMaster **你不能在对象上传播**,因为运算符是**数组传播运算符**,有一个名为 **object-spread-operator** 的提案,由 @CodingIntrigue 编写...顺便说一句,甚至“对象扩展运算符”也执行源的浅表复制...我认为您需要查看规范。 (2认同)