如何使用对象解构处理嵌套的默认参数?
How to handle nested default parameters with object destructuring?
我正在尝试弄清楚是否可以通过解构处理多级默认参数。由于不容易用文字来解释,这里是一步一步的例子...
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'}} = {}
。同样,如果在调用函数时没有给出参数,则会创建一个对象并提取核心属性(foo
和 bar
)。如果传递一个对象,只有未定义的属性(foo
或 bar
)将被解构为默认值。
问题是 bar
(quux
和 corge
)的对象属性不是 "top-level destructuring" 的一部分。这意味着 quux
或 corge
将是 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 - 具有深度默认参数的嵌套对象解构
我想在对象层次结构的所有级别设置默认参数以使用某种 "cascading destructuring"。我试过这个,但它不起作用:
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 是否允许这样的功能吗?如果是,我该如何实施?
解构对象属性的通用模式是
{ … , 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);
}
我有一些更简单的东西。它有缺点。不过先上货:
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
因此,即使示例函数设计为接受两个参数(arg1
和 hash
),签名也正式声明为 三个参数 .第三个参数是一种虚构的或临时的参数,它的存在仅仅是为了解构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 就此而言。
因此,此模式允许实现自我记录,而不依赖于开发人员需要编写和维护的额外文档。
缺点是该函数看起来像是接受三个参数——而且它确实接受三个。因此,不知道发生了什么的开发人员可能会被误导。而且,如果调用者传递三个参数,第三个参数将覆盖第二个参数。
怎么样
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
我正在尝试弄清楚是否可以通过解构处理多级默认参数。由于不容易用文字来解释,这里是一步一步的例子...
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'}} = {}
。同样,如果在调用函数时没有给出参数,则会创建一个对象并提取核心属性(foo
和 bar
)。如果传递一个对象,只有未定义的属性(foo
或 bar
)将被解构为默认值。
问题是 bar
(quux
和 corge
)的对象属性不是 "top-level destructuring" 的一部分。这意味着 quux
或 corge
将是 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 - 具有深度默认参数的嵌套对象解构
我想在对象层次结构的所有级别设置默认参数以使用某种 "cascading destructuring"。我试过这个,但它不起作用:
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 是否允许这样的功能吗?如果是,我该如何实施?
解构对象属性的通用模式是
{ … , 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);
}
我有一些更简单的东西。它有缺点。不过先上货:
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
因此,即使示例函数设计为接受两个参数(arg1
和 hash
),签名也正式声明为 三个参数 .第三个参数是一种虚构的或临时的参数,它的存在仅仅是为了解构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 就此而言。
因此,此模式允许实现自我记录,而不依赖于开发人员需要编写和维护的额外文档。
缺点是该函数看起来像是接受三个参数——而且它确实接受三个。因此,不知道发生了什么的开发人员可能会被误导。而且,如果调用者传递三个参数,第三个参数将覆盖第二个参数。
怎么样
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