【问题标题】:Destructuring objects as function parameters deep extend将对象解构为函数参数 深度扩展
【发布时间】:2017-04-19 19:13:57
【问题描述】:

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

例子:

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'
  }
});

这个输出:(baz 被覆盖)

changed
bar
{
  "propA": "changed"
}

是否有语法可以扩展baz 对象,以提供输出:

changed
bar
{
  "propA": "changed",
  "propB": "propB"
}

【问题讨论】:

    标签: javascript ecmascript-6


    【解决方案1】:

    没有本地方法可以按您的要求做,但是您可以自己写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"}})
    

    【讨论】:

      【解决方案2】:

      您还可以进行嵌套解构。你解构 baz,也解构 propApropB

      如果您需要整个对象并且不想“重建”它,这不适合您,但它确实实现了为嵌套在对象中的未指定属性设置默认值的目标。

      我将baz 保留为一个单独的描述值,并将其属性作为有时方便的模式的示例,但您很可能希望将其删除,因为它同样存在丢失默认属性的问题。

      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'
        }
      });

      【讨论】:

        【解决方案3】:

        由于参数解构允许您在其他参数的默认值中使用以前的参数,您可以创建一个不会退出原始参数的属性,例如__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'
          }
        });

        使用 object spread 的默认属性(需要 babel 插件 - 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'
          }
        });

        【讨论】:

        • 我没想到第二个,太好了!
        【解决方案4】:

        是的,完全有可能!

        解决方案 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`
        }
        

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

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

        你甚至可以这样做:

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

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

        示例 1:

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

        当您调用f1(obj) 时,obj.x 将被分配给varxf1 的范围内

        示例 2:

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

        类似于f1,但用{x, y}代替varxobj.xy将分配给{x, y},所以x = obj.xy.xy = obj.xy.y

        示例 3:现在让我们添加创建默认参数

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

        现在,您仍然需要将 obj 作为对象传递,但您不再需要提供 obj.xy。如果obj.xyundefinedxyundefineds

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

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

        如果你传递的obj.xyundefined{x, y}将被设置为{x: undefined, y: undefined},但由于xy也有默认值,x将是'XXX'和@987654357 @ 将是'YYY'

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

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

        我想我不必再解释了:D

        【讨论】:

        • @Hitmands 没有Object spread operator这样的概念,JS中的数组就是对象
        • @DMaster 是的,你在上面使用它。数组是对象,但对象不是数组。 ...{}object-rest-spread 提案
        • @Hitmands 对象不是数组,但这并不意味着您不能将数组“解构”为对象,因为数组是对象(您甚至可以使用字符串、数字等但不是 @ 987654362@ 和 undefined)。当你做...rest 时,rest 将自动成为一个数组
        • Nethier baz: { propA = 'propA', propB = 'propB' } = {}(编译错误)或baz = { propA = 'propA', propB = 'propB' } = {}(baz 被覆盖)为我工作
        • @DMaster 你不能在对象上展开,因为算子是数组展开算子,有一个提议叫object-spread-运算符 正如@CodingIntrigue 所写...顺便说一句,即使Object spread operator 也执行源的浅拷贝...我认为您需要查看规范。
        猜你喜欢
        • 2017-11-14
        • 1970-01-01
        • 1970-01-01
        • 2016-01-03
        • 1970-01-01
        • 1970-01-01
        • 2017-02-08
        • 2020-12-18
        • 1970-01-01
        相关资源
        最近更新 更多