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

时间:2017-05-10 20:00:29

标签: javascript function ecmascript-6 destructuring default-parameters

我试图弄清楚是否可以通过解构来处理多个级别的默认参数。由于用文字解释起来并不容易,这是一个循序渐进的例子......

1 - 使用默认参数

的扁平对象解构

解构这个对象很简单:

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

在函数签名中使用{foo = 'Foo', bar = 'Bar'} = {},如果在调用函数时没有传递参数,则将创建一个对象。如果传递了一个对象,但某些引用的属性为undefined,则它们将被默认值替换。这段代码工作正常:

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 - 使用浅默认参数

的嵌套对象解构

解构这个对象更难:

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

{foo = 'Foo', bar = 'Bar'} = {}不再是一个可行的选项,但现在我们可以在函数签名中使用{foo = 'Foo', bar = {quux: 'Quux', corge: 'Corge'}} = {}。同样,如果在调用函数时没有给出参数,则会创建一个对象并提取核心属性(foobar)。如果传递了一个对象,则只有未定义的属性(foobar)将使用其默认值进行解构。

问题是barquuxcorge)的对象属性不是“顶级解构”的一部分。这意味着quuxcorge如果undefined作为参数传递时未明确设置,则为bar

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 - 使用深度默认参数

的嵌套对象解构

我想在对象层次结构的所有级别设置默认参数,以使用一种“级联解构”。我试过这个,但它不起作用:

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

您知道ES6中是否允许使用此功能吗?如果是,我该如何实施呢?

3 个答案:

答案 0 :(得分:7)

解构对象属性的通用模式是

{ … , propertyName: target = defaultInitialiser, … }

(当属性名称与目标变量标识符完全相同时,我们可以加入它们。)

但是target不仅适用于变量,它可以是任何赋值目标 - 包括嵌套的解构表达式。因此,对于您的情况(3),您希望在参数的顶层使用与(1)完全相同的方法 - 默认使用空对象初始化属性并对其部分进行解构:

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

请注意,在解构该属性时没有bar变量。如果你想为属性引入一个bar变量,你可以重复属性名称并执行

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

答案 1 :(得分:0)

我有一些简单的东西。它有缺点。但首先,货物:

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

这有什么作用?

  • 为所有位置参数提供名称,包括命名参数的哈希值
  • 解构所有命名参数
  • 为每个参数提供默认值,无论是位置参数还是命名参数

它如何工作?

ES6参数默认值可以refer to other parameters,如下所示:

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

因此,即使示例函数被设计为接受两个参数(arg1hash),签名还是由三个参数正式声明。第三个参数是一种虚构的或临时的参数,仅出于解构hash的目的而存在。这在逻辑上是等效的:

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

此模式的优点在于签名是完全自我记录的。不幸的是,在JS中看到这样声明的签名很常见:

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

要回答该问题,您需要搜索所有下游代码路径并收集对options的每次使用。有时,该代码路径确实很长;如果您很不幸无法在基于微服务的生态系统中工作,那么该代码路径可以跨越其他语言的两个或多个其他代码库(真实情况)。

是的,我们可以要求开发人员编写文档,但是YMMV可以满足要求。

因此,这种模式允许实现实现自我记录,而无需依赖开发人员需要编写和维护的额外文档。

缺点是该函数看起来像它接受三个参数,而确实做到接受三个参数。因此,可能会误导那些不知道发生了什么的开发人员。而且,如果调用者传递了三个arg,则第三个arg将覆盖第二个arg。

答案 2 :(得分:0)

关于

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