在不使用返回值的情况下执行映射操作是反模式吗?

IT技术 javascript functional-programming map-function
2021-02-07 12:42:38

假设我有一个列表,我想使用映射函数向其中添加一些值:

const arr = [1, 2, 3, 4, 5];
const anotherArr = [];

我使用函数式方法来做到这一点:

arr.map((item) => anotherArr.push(item));

这是一个反模式/错误的逻辑——也就是说,不使用任何映射操作的返回值?这方面有什么好的资源吗?

(我知道这个逻辑很愚蠢,我可以复制列表 - 这不是我的问题的重点)

1个回答

是的,这是一种反模式。尽管您可能会争辩说它不是,但它只是简单的误用。我把它叫做一个反模式,因为某些原因人们经常普遍使用的事件.map()时,他们不应该。

该术语来自数学,您可以在其中从一个类别映射到另一个类别。例如,形状 (X) 到颜色 (Y):

“一种类型的地图是一种函数,就像 X 中四种颜色形状中的任何一种与其 Y 中的颜色的关联。”  ——来自维基百科的描述 (图片来自维基百科)

该术语在计算机科学也很成熟,其中map()进行此类转换的高阶函数。在 JavaScript 中,它是一种数组方法,用法很明确——将一个数组的内容转换为另一个数组。给定数组,X = [0, 5, 8, 3, 2, 1]我们可以x => x + 1使用该.map()方法对其进行应用

“在列表上应用地图功能时的处理步骤视图”--来自维基百科的描述 (图片来自维基百科)

这不仅仅是实现的细节影响更广泛 -.map()惯用的,如果误用会使代码更难阅读和理解。让我们做一个循序渐进的例子:

我们需要一个映射函数来表达元素之间的关系。例如,将一个字母转换为它在字母表中的位置可以通过以下函数表示:

function letterToPositionInAlphabet(letter) {
  return letter.toUpperCase().charCodeAt(0) - 64;
}

通过这个函数映射一个字母数组将为我们提供一个包含它们每个位置的数组:

function letterToPositionInAlphabet(letter) {
  return letter.toUpperCase().charCodeAt(0) - 64;
}

const letters = ["a", "b", "c"];

console.log(letters.map(letterToPositionInAlphabet));

映射操作是一种习惯用法,也是理解代码的一部分。如果您看到someArr.map(someFn)它设置了预期并且很容易理解正在发生的操作类型,而无需知道数组或函数的内容。当你看到letters.map(letterToPositionInAlphabet)它的意图是什么应该是微不足道的 - 获取一些字母在字母表中的位置。这是自记录代码,我们可以假设代码是正确的,除非另有证明。

但是,使用.map()as.forEach()会破坏预期的含义,阅读起来可能会令人困惑。考虑这个

function playerToPlaceInRankList(player) {
   const position = lookupPlayerRank(player);
   positionsArr.push(position);
}

/* many lines later */

players.map(playerToPlaceInRankList);

/* more code */

该行好像它执行也映射,因为返回值被忽略立即看起来是错误的。要么不需要该行,要么您必须检查有什么playerToPlaceInRankList作用才能找出此处实际发生的情况。对于仅仅阅读应该是直接和自我记录的代码行来说,这是不必要的心理负担。

这同样适用于使用其他方法,如.filter().find().every().some()等。不要仅仅因为它们遍历数组而使用它们,如果您想要的不是它们的本意。