javascript es6:解构剩余参数的用例
javascript es6: use case for destructuring rest parameter
我刚刚在 MDN 中看到一个关于解构其余参数的代码片段,如下所示:
function f(...[a, b, c]) {
return a + b + c;
}
f(1) // NaN (b and c are undefined)
f(1, 2, 3) // 6
f(1, 2, 3, 4) // 6 (the fourth parameter is not destructured)
代码片段在此页面中:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
虽然剩余参数的常见用例对我来说非常清楚 (function foo(...params){/*code*/}
),但我无法想象一个真实世界的用例来使用剩余参数,就像该代码片段中呈现的方式一样。相反,我认为在那种情况下,我应该只使用一个通用函数定义:
function f(a, b, c) {
return a + b + c;
}
f(1) // NaN (b and c are undefined)
f(1, 2, 3) // 6
f(1, 2, 3, 4) // 6 (the fourth parameter is not defined)
你的function f(a, b, c) { … }
确实是正确的写法。它与 rest+destructuring 语法之间的唯一区别是 rest 参数不会增加参数的数量,即 f.length == 0
.
将数组解构模式作为剩余参数的目标确实没有好的用例。仅仅因为语法允许它并不意味着它在某个地方有用。 MDN 示例可能应该更清楚。
这个例子说明了 rest 和 destructuring 语法足够灵活,甚至可以这样组合。
据了解,目前 TypeScript nor Babel 稳定版本均不支持此语法,主要是因为它没有实际用途。
其实...
运算符有两种方式。根据您的用例,它被称为 rest 和 spread 。它们都是非常强大的运算符,尤其是对于函数式方法。您可以始终使用传播运算符作为,
var a = [1,2,3],
b = [4,5,6];
a.push(...b);
这会使 a
一下子变为 [1,2,3,4,5,6]
。此时此刻,可以说 .concat()
也可以做到这一点。是的,concat 具有内置的传播功能,但 a.concat(b)
不会影响 a
。我刚刚创建 returns 一个新数组。事实上,在适当的函数式语言中,为了纯粹起见,将 a
视为不可变对象是很好的。然而 JS 是一种奇怪的语言。它被认为是函数式的,但同时深深地包含了引用类型。长话短说,如果你想在改变它的同时保持对 a
的引用完整,那么你不能使用 a.concat(b)
但 a.push(...b)
。在这里我不得不提到 .push()
并不是完美的设计,因为它 returns 一个愚蠢的长度 属性 完全没用 。它应该返回 a
。所以我最终大部分时间都使用像 (a.push(...b),a)
这样的逗号运算符。
好的,除了简单的用例之外,您还可以 ...
进一步扩展以获得更复杂但看起来更酷的实现。比如你可以做一个 Haskellesque 模式匹配来分割数组的头和尾并相应地递归。
这是展开和剩余运算符协同工作以展平任意嵌套数组的有用案例。
var flat = (x,...xs) => x ? [...Array.isArray(x) ? flat(...x) : [x], ...flat(...xs)] : [];
var na = [[1,2],[3,[4,5]],[6,7,[[[8],9]]],10];
fa = flat(na);
console.log(fa);
假设我们有一个函数 returns 像这样的对象:
function getCustomer(id) {
return fetch(`http://myapi.com/customer/${id}`);
}
假设我有这样的回复:
{
"customer": {
"id": 1234,
"name": "John Doe",
"latestBadges": [
"Platinum Customer",
"100 Buys",
"Reviewer"
]
}
}
在更传统的方法中,我可以编写一个函数来显示最新的 3 个徽章,如下所示:
function showLatestBadges(a, b, c) {
console.log(a, b, c);
}
要使用该功能,我需要:
getCustomer(1234).then((customer) => {
showLatestBadges(
customer.latestBadges[0],
customer.latestBadges[1],
customer.latestBadges[2]
);
});
有了这个新的传播运算符,我可以改为这样做:
getCustomer(1234).then((customer) => {
showLatestBadges(...customer.latestBadges);
});
因此,在函数定义中使用扩展运算符可能看起来有点无用。但是,事实上,它在非常特殊的情况下很有用:
假设我们有一个遗留系统,假设在数百个地方调用 showLatestBadges
函数而不使用扩展运算符,就像过去一样。我们还假设我们正在使用一个 linting 工具来防止未使用的变量,并且我们还假设我们是 运行 一个关心 linting 结果的构建过程,如果 linting 说有什么地方不对,构建失败。
我们还假设对于一些奇怪的业务规则,我们现在只需要显示第一个和第三个徽章。
现在,假设这个函数调用是在遗留系统的数百个地方进行的,而我们没有太多时间来交付这个新业务规则的实现,我们就没有时间为所有这数百个调用重构代码.
因此,我们现在将函数更改为:
function showLatestBadges(a, b, c) {
console.log(a, c);
}
但现在我们遇到了一个问题:由于未使用 b
变量,构建失败,我们必须在 YESTERDAY 交付此更改!!!我们没有时间重构对该函数的所有数百次调用,我们不能只在所有位置进行简单的查找和替换,因为我们的代码如此混乱,到处都是 eval 和不可预测的行为可能发生。
因此,一种解决方案是:使用扩展运算符更改函数签名,以便构建成功,并在板上创建一个任务来进行重构。
所以,我们可以这样修改函数:
function showLatestBadges(...[a,,c]) {
console.log(a, c);
}
好的,我知道这是一个非常特殊的情况,而且这不太可能发生,但是,谁知道呢? ¯\_(ツ)_/¯
这是我必须使用它的用例之一
const tail = function([, ...xs]) {
return xs;
}
tail([1,2]); // [2]
const head = ([a]) => a
head([1,2,3,4]) // 1
我刚刚在 MDN 中看到一个关于解构其余参数的代码片段,如下所示:
function f(...[a, b, c]) {
return a + b + c;
}
f(1) // NaN (b and c are undefined)
f(1, 2, 3) // 6
f(1, 2, 3, 4) // 6 (the fourth parameter is not destructured)
代码片段在此页面中:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
虽然剩余参数的常见用例对我来说非常清楚 (function foo(...params){/*code*/}
),但我无法想象一个真实世界的用例来使用剩余参数,就像该代码片段中呈现的方式一样。相反,我认为在那种情况下,我应该只使用一个通用函数定义:
function f(a, b, c) {
return a + b + c;
}
f(1) // NaN (b and c are undefined)
f(1, 2, 3) // 6
f(1, 2, 3, 4) // 6 (the fourth parameter is not defined)
你的function f(a, b, c) { … }
确实是正确的写法。它与 rest+destructuring 语法之间的唯一区别是 rest 参数不会增加参数的数量,即 f.length == 0
.
将数组解构模式作为剩余参数的目标确实没有好的用例。仅仅因为语法允许它并不意味着它在某个地方有用。 MDN 示例可能应该更清楚。
这个例子说明了 rest 和 destructuring 语法足够灵活,甚至可以这样组合。
据了解,目前 TypeScript nor Babel 稳定版本均不支持此语法,主要是因为它没有实际用途。
其实...
运算符有两种方式。根据您的用例,它被称为 rest 和 spread 。它们都是非常强大的运算符,尤其是对于函数式方法。您可以始终使用传播运算符作为,
var a = [1,2,3],
b = [4,5,6];
a.push(...b);
这会使 a
一下子变为 [1,2,3,4,5,6]
。此时此刻,可以说 .concat()
也可以做到这一点。是的,concat 具有内置的传播功能,但 a.concat(b)
不会影响 a
。我刚刚创建 returns 一个新数组。事实上,在适当的函数式语言中,为了纯粹起见,将 a
视为不可变对象是很好的。然而 JS 是一种奇怪的语言。它被认为是函数式的,但同时深深地包含了引用类型。长话短说,如果你想在改变它的同时保持对 a
的引用完整,那么你不能使用 a.concat(b)
但 a.push(...b)
。在这里我不得不提到 .push()
并不是完美的设计,因为它 returns 一个愚蠢的长度 属性 完全没用 。它应该返回 a
。所以我最终大部分时间都使用像 (a.push(...b),a)
这样的逗号运算符。
好的,除了简单的用例之外,您还可以 ...
进一步扩展以获得更复杂但看起来更酷的实现。比如你可以做一个 Haskellesque 模式匹配来分割数组的头和尾并相应地递归。
这是展开和剩余运算符协同工作以展平任意嵌套数组的有用案例。
var flat = (x,...xs) => x ? [...Array.isArray(x) ? flat(...x) : [x], ...flat(...xs)] : [];
var na = [[1,2],[3,[4,5]],[6,7,[[[8],9]]],10];
fa = flat(na);
console.log(fa);
假设我们有一个函数 returns 像这样的对象:
function getCustomer(id) {
return fetch(`http://myapi.com/customer/${id}`);
}
假设我有这样的回复:
{
"customer": {
"id": 1234,
"name": "John Doe",
"latestBadges": [
"Platinum Customer",
"100 Buys",
"Reviewer"
]
}
}
在更传统的方法中,我可以编写一个函数来显示最新的 3 个徽章,如下所示:
function showLatestBadges(a, b, c) {
console.log(a, b, c);
}
要使用该功能,我需要:
getCustomer(1234).then((customer) => {
showLatestBadges(
customer.latestBadges[0],
customer.latestBadges[1],
customer.latestBadges[2]
);
});
有了这个新的传播运算符,我可以改为这样做:
getCustomer(1234).then((customer) => {
showLatestBadges(...customer.latestBadges);
});
因此,在函数定义中使用扩展运算符可能看起来有点无用。但是,事实上,它在非常特殊的情况下很有用:
假设我们有一个遗留系统,假设在数百个地方调用 showLatestBadges
函数而不使用扩展运算符,就像过去一样。我们还假设我们正在使用一个 linting 工具来防止未使用的变量,并且我们还假设我们是 运行 一个关心 linting 结果的构建过程,如果 linting 说有什么地方不对,构建失败。
我们还假设对于一些奇怪的业务规则,我们现在只需要显示第一个和第三个徽章。
现在,假设这个函数调用是在遗留系统的数百个地方进行的,而我们没有太多时间来交付这个新业务规则的实现,我们就没有时间为所有这数百个调用重构代码.
因此,我们现在将函数更改为:
function showLatestBadges(a, b, c) {
console.log(a, c);
}
但现在我们遇到了一个问题:由于未使用 b
变量,构建失败,我们必须在 YESTERDAY 交付此更改!!!我们没有时间重构对该函数的所有数百次调用,我们不能只在所有位置进行简单的查找和替换,因为我们的代码如此混乱,到处都是 eval 和不可预测的行为可能发生。
因此,一种解决方案是:使用扩展运算符更改函数签名,以便构建成功,并在板上创建一个任务来进行重构。 所以,我们可以这样修改函数:
function showLatestBadges(...[a,,c]) {
console.log(a, c);
}
好的,我知道这是一个非常特殊的情况,而且这不太可能发生,但是,谁知道呢? ¯\_(ツ)_/¯
这是我必须使用它的用例之一
const tail = function([, ...xs]) {
return xs;
}
tail([1,2]); // [2]
const head = ([a]) => a
head([1,2,3,4]) // 1