javascript es6:用例解析rest参数

时间:2017-12-29 17:27:48

标签: javascript ecmascript-6

我刚刚在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)

5 个答案:

答案 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)

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

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

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

好的,我知道这是一个非常具体的情况,这与发生的情况非常不同,但是,谁知道呢? ¯\ _(ツ)_ /¯