8

I just saw a code snippet in MDN about destructuring rest parameters like so:

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)

the code snippet is in this page: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters

Although the common use case for rest parameters is very clear to me (function foo(...params){/*code*/}) I could not think about a real world use case to use rest parameters like the way presented in that code snippet. Instead, I think that in that case, I should just use a common function definition:

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

5 回答 5

4

function f(a, b, c) { … }确实是写这个的正确方法。这与 rest+destructuring 语法之间的唯一区别是,rest 参数不会添加到参数的数量,即f.length == 0.

将数组解构模式作为休息参数的目标确实没有很好的用例。仅仅因为语法允许它并不意味着它在某处有用。MDN 示例可能应该更清楚地说明这一点。

于 2017-12-29T17:49:44.770 回答
1

假设我们有一个返回这样一个对象的函数:

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

好的,我知道这是一个非常特殊的情况,而且这种情况不太可能发生,但是,谁知道呢?¯\_(ツ)_/¯

于 2018-04-09T14:30:01.950 回答
1

该示例说明了休息和解构语法足够灵活,即使以这种方式也可以组合。

众所周知,TypeScriptBabel稳定版本目前都不支持这种语法,主要是因为它没有实际用途。

于 2018-02-12T18:14:34.993 回答
-1

这是我必须使用它的用例之一

const tail = function([, ...xs]) { return xs; } tail([1,2]); // [2]

const head = ([a]) => a
head([1,2,3,4]) // 1
于 2018-10-31T05:06:37.133 回答
-1

实际上...运营商是两种方式。根据您的用例,它既称为休息又称为传播。它们都是非常强大的运算符,尤其是对于函数式方法。您可以始终使用扩展运算符,

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)but 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);

于 2018-04-06T23:51:18.640 回答