我刚刚在MDN中看到了一个关于解析rest参数的代码片段,如下所示:
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)
答案 0 :(得分:4)
你的function f(a, b, c) { … }
确实是写这个的正确方法。它与其余+解构语法之间的唯一区别是,rest参数不会增加参数的数量,即f.length == 0
。
将数组解构模式作为rest参数的目标确实没有好的用例。仅仅因为语法允许它并不意味着它在某个地方有用。 MDN示例可能应该更清楚。
答案 1 :(得分:1)
实际上...
运营商有两种方式。它根据您的使用情况称为 rest 和 spread 。它们都是非常强大的运算符,尤其是功能方法。您可以始终使用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
。我只是创建并返回一个新数组。实际上,在适当的函数式语言中,将a
视为不可变对象对于纯度来说是很好的。然而,JS是一种奇怪的语言。它被认为是功能性的,但同时深深地包含了参考类型。如果你想在保持对a
的引用完整的同时保持原样,那么你就不能使用a.concat(b)
而是使用a.push(...b)
。在这里,我必须提到.push()
并不是完美设计的,因为它返回了一个愚蠢的长度属性, 完全没用 。它本应返回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);

答案 2 :(得分:1)
这是我要使用的用例之一
const tail = function([, ...xs]) {
return xs;
}
tail([1,2]); // [2]
const head = ([a]) => a
head([1,2,3,4]) // 1
答案 3 :(得分:0)
该示例说明了休息和解构语法足够灵活,甚至可以以这种方式组合。
众所周知,TypeScript和Babel稳定版本目前都不支持此语法,主要是因为它没有实际用途。
答案 4 :(得分:0)
假设我们有一个返回类似对象的函数:
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);
});
因此,在函数定义中使用spread运算符可能看起来有点无用。但实际上,它在非常具体的情况下非常有用:
假设我们有一个遗留系统,让我们说showLatestBadges
函数的调用是在数百个地方进行的,而不使用传播运算符,就像过去一样。我们还假设我们正在使用一个防止未使用变量的linting工具,并且还假设我们正在运行一个关注linting结果的构建过程,如果linting说某些东西不正确,则构建失败。
让我们也相信,对于一些奇怪的商业规则,我们现在只需要显示第一和第三个徽章。
现在,假设此函数调用是在遗留系统中的数百个位置进行的,并且我们没有太多时间来提供此新业务规则的实现,我们没有时间重构所有这些数百个调用的代码。
所以,我们现在将改变函数:
function showLatestBadges(a, b, c) {
console.log(a, c);
}
但现在我们遇到了一个问题:由于未使用的b
变量,构建失败,我们必须在昨天提供此更改!我们没有时间重构这个函数的所有数百个调用,我们不能只在所有的位置进行简单的查找和替换,因为我们有这样一个混乱的代码,并且遍布整个地方有不可预测的行为和不可预测的行为可能会发生。
因此,一个解决方案是:使用扩展运算符更改函数签名,以便构建成功,并在板上创建任务以进行重构。 所以,我们可以改变这个功能:
function showLatestBadges(...[a,,c]) {
console.log(a, c);
}
好的,我知道这是一个非常具体的情况,这与发生的情况非常不同,但是,谁知道呢? ¯\ _(ツ)_ /¯