简体   繁体   English

如何使用对象解构处理嵌套的默认参数?

[英]How to handle nested default parameters with object destructuring?

I am trying to figure out if it is possible to handle multiple levels of default parameters with destructuring.我试图弄清楚是否可以通过解构处理多个级别的默认参数。 Since it is not easy to explain with words, here is a step-by-step example...由于不容易用文字解释,这里是一个循序渐进的例子......


1 - Flat object destructuring with default parameters 1 - 使用默认参数的平面对象解构

Destructuring this object is easy:解构这个对象很容易:

let obj = {
  foo: 'Foo',
  bar: 'Bar'
};

With {foo = 'Foo', bar = 'Bar'} = {} in a function signature, an object will be created if there is no argument passed when the function is called.使用{foo = 'Foo', bar = 'Bar'} = {}在函数签名中,如果在调用函数时没有传递参数,将创建一个对象。 If an object is passed but some referenced properties are undefined , they will be replaced by their default values.如果传递了一个对象,但某些引用的属性undefined ,则它们将替换为其默认值。 This code works fine:这段代码工作正常:

 function fn1({foo = 'Foo', bar = 'Bar'} = {}) { console.log(foo, bar); } // OK fn1(); // Foo Bar fn1({foo: 'Quux'}); // Quux Bar fn1({bar: 'Quux'}); // Foo Quux fn1({foo: 'Quux', bar: 'Quux'}); // Quux Quux

2 - Nested object destructuring with shallow default parameters 2 - 使用浅层默认参数的嵌套对象解构

Destructuring this object is harder:解构这个对象更难:

let obj = {
  foo: 'Foo',
  bar: {
    quux: 'Quux',
    corge: 'Corge'
  }
};

{foo = 'Foo', bar = 'Bar'} = {} is not a viable option anymore, but now we can use {foo = 'Foo', bar = {quux: 'Quux', corge: 'Corge'}} = {} in a function signature. {foo = 'Foo', bar = 'Bar'} = {}不再是一个可行的选项,但现在我们可以使用{foo = 'Foo', bar = {quux: 'Quux', corge: 'Corge'}} = {}在函数签名中。 Again, if no argument is given when the function is called, an object is created and the core properties ( foo and bar ) are extracted.同样,如果在调用函数时没有给出参数,则会创建一个对象并提取核心属性( foobar )。 If an object is passed, only undefined properties ( foo or bar ) will be destructured with their default values.如果传递了一个对象,则只有未定义的属性( foobar )将使用它们的默认值进行解构。

The problem is that the object properties of bar ( quux and corge ) are not part of the "top-level destructuring".问题是barquuxcorge )的对象属性不是“顶级解构”的一部分。 This means quux or corge will be undefined if they are not explicitly set when bar is passed as an argument:这意味着当bar作为参数传递时,如果没有明确设置quuxcorge将是undefined的:

 function fn2({foo = 'Foo', bar = {quux: 'Quux', corge: 'Corge'}} = {}) { console.log(foo, bar.quux, bar.corge); } // OK fn2(); // Foo Quux Corge fn2({foo: 'Quux'}); // Quux Quux Corge // Oops! fn2({bar: {quux: 'Baz'}}); // Foo Baz undefined fn2({foo: 'Quux', bar: {corge: 'Baz'}}); // Quux undefined Baz

3 - Nested object destructuring with deep default parameters 3 - 嵌套对象解构与深度默认参数

I would like to set default parameters at all levels of the object hierarchy to use a sort of "cascading destructuring".我想在对象层次结构的所有级别设置默认参数以使用一种“级联解构”。 I tried this, but it does not work:我试过这个,但它不起作用:

 function fn3({foo = 'Foo', bar = ({quux = 'Quux', corge = 'Corge'} = {})} = {}) { console.log(foo, bar.quux, bar.corge); } // Oops! fn3(); // Foo undefined undefined fn3({foo: 'Quux'}); // Quux undefined undefined fn3({bar: {quux: 'Baz'}}); // Foo Baz undefined fn3({foo: 'Quux', bar: {corge: 'Baz'}}); // Quux undefined Baz


Do you know if such a feature is allowed in ES6.你知道在 ES6 中是否允许这样的特性。 If yes, how can I implement it?如果是,我该如何实施?

The generic pattern for destructuring object properties is解构对象属性的通用模式是

{ … , propertyName: target = defaultInitialiser, … }

(when the property name is exactly the same as the target variable identifier we can join them). (当属性名称与目标变量标识符完全相同时,我们可以将它们连接起来)。

But target is not only for variables, it can be any assignment target - including nested destructuring expressions.但是target不仅适用于变量,它还可以是任何赋值目标——包括嵌套的解构表达式。 So for your case (3) you want to use exactly the same approach as with (1) on the top level of the parameter - default initialise the property with an empty object and destructure its parts:因此,对于您的情况 (3),您希望在参数的顶层使用与 (1) 完全相同的方法 - 默认使用空对象初始化属性并解构其部分:

function fn3({foo = 'Foo', bar: {quux = 'Quux', corge = 'Corge'} = {}} = {}) {
  console.log(foo, quux, corge);
}

Notice that there is no bar variable when you destructure that property.请注意,解构该属性时没有bar变量。 If you want to introduce a bar variable for the property as well, you could repeat the property name and do如果您还想为属性引入bar变量,您可以重复属性名称并执行

function fn3({foo = 'Foo', bar, bar: {quux = 'Quux', corge = 'Corge'} = {}} = {}) {
  console.log(foo, bar, quux, corge);
}

I've got something that's a little simpler.我有一些更简单的东西。 It has drawbacks.它有缺点。 But first, the goods:但首先,货物:

function doit( arg1 = 'one', hash = { prop1: 'two', prop2: 'three' }, { prop1, prop2 } = hash ) {
    console.log(`arg1`, arg1)
    console.log(`prop1`, prop1)
    console.log(`prop2`, prop2)
    console.log(`hash`, hash)
}

What does this accomplish?这有什么作用?

  • provides names for all positional arguments, including the hash of named arguments提供所有位置参数的名称,包括命名参数的散列
  • destructures all named arguments解构所有命名参数
  • provides a default value for every argument, whether positional or named为每个参数提供默认值,无论是位置参数还是命名参数

How does it work?它是如何工作的?

ES6 parameter defaults can refer to other parameters , like so: ES6 参数默认值可以引用其他参数,如下所示:

function math( x = 1, y = x ) { ... }
// x = 1
// y = 1

So, even though the example function is designed to accept two arguments ( arg1 and hash ), the signature is formally declared with three arguments .因此,即使示例函数设计为接受两个参数( arg1hash ),签名还是用三个参数正式声明。 The third argument is a kind of fictional or temporary argument that exists solely for the purpose of destructuring hash .第三个论点是一种虚构的或临时性的论点,仅存在于解构hash的目的。 It is the logical equivalent of this:这是逻辑上的等价物:

function doit( arg1 = 'one', hash = { prop1: 'two', prop2: 'three' } ) {
    let { prop1, prop2 } = hash
    ...
}

The virtue of this pattern is that the signature is completely self-documenting.这种模式的优点是签名是完全自记录的。 It's sadly very common in JS to see signatures declared like this:遗憾的是,在 JS 中看到这样声明的签名很常见:

function terminateEmployee( employeeId, options ) {
    // what properties does `options` accept??
}

To answer that question, you need to search all downstream codepaths and collect every use of options .要回答这个问题,您需要搜索所有下游代码路径并收集options每次使用。 Sometimes that codepath is really long;有时代码路径真的很长; if you're unlucky enough to be working in a microservice-based ecosystem, that codepath can span two or more additional codebases in other languages (true story).如果您不幸在基于微服务的生态系统中工作,那么该代码路径可以跨越两个或多个其他语言的附加代码库(真实故事)。

Yes, we can ask devs to write documentation, but YMMV on that score.是的,我们可以要求开发人员编写文档,但在这个分数上是 YMMV。

So, this pattern allows the implementation to be self-documenting, without relying on extra documentation that devs would need to write and maintain.因此,这种模式允许实现自我记录,而无需依赖开发人员需要编写和维护的额外文档。

The downside is that the function looks like it accepts three arguments -- and it really does accept three.缺点是该函数看起来接受三个参数——但它确实接受三个参数。 So, devs who are unaware of what's going on can be misled.因此,不知道发生了什么的开发人员可能会被误导。 And, if a caller passes three args, the third arg will override the second arg.并且,如果调用者传递三个参数,第三个参数将覆盖第二个参数。

what about关于什么

 function fn3({foo = 'Foo', bar={} } = {}) { const {quux = 'Quux', corge = 'Corge'} = bar; console.log(foo, quux, corge); } fn3(); // Foo Quux Corge fn3({foo: 'Quux'}); // Quux Quux Corge fn3({bar: {quux: 'Baz'}}); // Foo Baz Corge fn3({foo: 'Quux', bar: {corge: 'Baz'}}); // Quux Quux Baz

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM