箭头函数中不使用花括号的多个表达式写法是什么意思?

3

我喜欢探索JavaScript的限制,想知道是否可以在箭头函数中使用最后一个表达式作为返回语句。

幸运的是,这是可能的,但在同一范围内设置变量有一些缺点。

[1,2,3,4,5].reduce((sum, element) => (const doubleSum=sum+sum, doubleSum+element));

这会返回未识别的标识符(指doubleSum)

解决方法是改为如下编写:

let doubleSum; 
[1,2,3,4,5].reduce((sum, element) => (doubleSum=sum+sum, doubleSum+element));

现在不需要花括号,通过函数内的两个表达式我可以得到sum+sum+element的总和。这并不是一个很好的使用示例,但它展示了如何在没有花括号的情况下使用两个表达式。

这个结果将会是57,因为每次迭代只返回doubleSum+element。如果你想把计算分成更易读的部分,这种方法非常适用。

另一个例子是在没有使用花括号的情况下记录每次迭代的结果。

[1,2,3,4,5].reduce((sum, element) => (console.log(sum), sum+sum+element));
//1
//4
//11
//26
//57

我非常喜欢这种语法,但我想知道它是否是不好的实践,因为在箭头函数中没有提到。


2
This returns unidentified identifier” - 我得到了“Unexpected token const”的错误。很明显这是语法错误,不是吗? - Bergi
4个回答

3

我想知道是否有不良实践

是的,在数组函数之外声明doubleSum绝对是一种不良实践。不要这样做。

另一个例子是在没有花括号的情况下使用console.log。我真的很喜欢这种语法,但箭头函数中没有提到它

是的,那是因为它不特定于箭头函数。你只是在这里使用分组语法逗号运算符。一些类似的用法可以在那里的处理后再返回使用示例中找到。


2
你所提到的,是逗号运算符 (MDN entry)。
这个运算符来自C语言,其用例通常是声明多个变量(var i, j, k)和特定宏定义(这些宏定义非常麻烦)。
大多数现代语言为了更高级的抽象性而放弃了使用这个运算符。
但即使我们忽略历史,我认为最佳实践的主要目标之一应该是可读性属性。目标肯定不是为了节省几个按键。
现在来比较一下:
let doubleSum; 
[1,2,3,4,5].reduce((sum, element) => (doubleSum=sum+sum, doubleSum+element));

并且

[1,2,3,4,5].reduce((sum, element) =>
{
    const doubleSum = sum + sum;
    return doubleSum + element;
});

其中一个示例将doubleSum声明为闭合变量,用于所有reduce迭代,而另一个则将doubleSum声明为每次迭代的本地变量。你认为哪个更好地表达了意图?哪一个更容易重构而不担心破坏某些奇怪的情况?

在日志记录示例中,这并不是很清楚。然而,通常认为将每个副作用(如记录消息)放入单独的语句中(以;结尾的行)是一种良好的实践。

想象一下:

someArray.map((element) => (console.log(element), doSomething(element)));

然而,由于某种原因,doSomething 没有按照您的期望工作。让我们添加一些日志记录:

let intermediate;
someArray.map((element) => (
    console.log(element),
    intermediate = doSometing(element),
    console.log(intermediate),
    intermediate));

我们又遇到了与“intermediate”有关的奇怪作用域问题。这个更改可能并不容易 - 你想在“map”回调中做一些事情,但实际上你必须在其范围之外创建一个变量才能做到这一点。现在想象一下,映射回调的复杂性会增加,需要几个这样的绑定变量。或者可能有更多的if/else分支和变量。然后,你可能想将其提取到一个单独的函数中:现在你需要再次考虑所有绑定变量。
那么使用块如何呢?
someArray.map((element) =>
{
    console.log(element);
    doSomething(element);
});

变成

someArray.map((element) =>
{
    console.log(element);
    const intermediate = doSometing(element);
    console.log(intermediate);
    return intermediate;
});

现在,我们只需要编辑地图回调的内部块,改变变得更容易做到和理解。引入更多变量?没问题,它们都留在回调函数中。将回调函数提取为单独的函数?也没有问题。这段代码的意图很清楚。


你的示例如果没有分组语法是无法编译的。对我来说,它非常易读。但是,我确实认为intermediate的作用域是一个问题,从我所阅读的内容来看,这似乎是一种不好的做法。 - Pavlo
@Pavlo,感谢您的反馈,我已经扩展了答案并修复了代码示例。 - Zdeněk Jelínek

1

你不能在表达式中声明变量。但是你可以构建一个函数表达式并立即调用它(称为IIFE),这样你就有了局部参数:

 [1, 2, 3, 4].reduce((sum, value) => (double => double + value)(sum + sum));

如果那是有用的另一回事。


1
是的,我也使用了这个解决方案 [1, 2, 3, 4, 5].reduce((sum, element) => element + (currentSum => currentSum + currentSum)(sum));,这似乎是最明智的解决方案,可能也应该提到逗号分隔。 - Pavlo

0

我认为你想要实现的目标可能会让代码难以阅读。在箭头函数中使用花括号并没有什么问题。如果你想保持 reduce 回调的简洁性,你可以尝试像这样做:

const doubleSumAndAddElement = (sum, element) => {
    console.log(sum);
    return 2 * sum + element;
}

[1,2,3,4,5].reduce(doubleSumAndAddElement);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接