JS 应该使用 for 还是 map

我猜你在看本文之前一定看过很多关于 forforEachmapfilter 性能分析的文章。但是我认为,性能不是你选择使用for还是map时应该关注的重点。性能问题你往往 80%的场景下都遇不到,你更应当关注的是哪种风格能够减少出 Bug 的可能性,以及哪种风格更易于后续维护。

你应该仅在出现或预测到性能问题时,针对性的优化相关代码。其他时候,应优先保证代码可靠性和可维护性。性能问题往往会导致业务系统操作缓慢,我们可以通过限流和增加服务器的手段暂时性缓解问题,直到完成性能优化;但是 Bug 和难以维护的代码将会让你的业务付出惨痛的代价和无法估量的成本。

研发间流传着一个笑话,为了保住饭碗。应该尽可能减少注释。 如果将所有mapfilter都换成for,那这工作就是铁饭碗了。

应该优先选择mapfilter

如果你遇到需要转换一个数组或者筛选数组元素的时候,你总是应该优先选择mapfilter解决问题。如果你熟悉ramda库,配合其一起使用将有效的提升代码可读性。让我们看一些例子:

const arr = [1, 2, 3]; // 使用 for const newArr = []; for (const num of arr) { newArr.push(num + 1); } // 使用 map const newArr = arr.map((num) => num + 1); // 使用 map 配合 ramda const newArr = arr.map(R.inc);

没有人会认为mapfor的可读性更差。但是不少人忽略的一点,map不需要一个可变的newArr

习惯使用 JS 开发的可能会很疑惑,newArr都是const,哪里存在可变? 让我们关注newArr.push(num + 1)这一行,它在主动修改newArr变量。 而map并不会主动修改newArr。在 JS 中,const仅代表了数组/对象的引用不变,而不是数据不变。

可是newArr.push(num + 1)这又有什么问题呢?

这里的问题非常大且隐蔽:使用map消除了可变的newArr,意味着你不会不小心将.push写成.shift而产生 Bug。map消除了你产生 Bug 的可能性!

ramda是一个非常优秀的基础函数库,可以大大增加代码可读性。 ramda宣扬函数式可能会引来一些人对其嗤之以鼻。 我想这些人一定是遇到了将几十个函数串成一个糖葫芦的代码。 并因此认为函数式等价于晦涩难懂。

我也遇到过这种代码(正是我函数式的启蒙导师写出来的), 我认为这是非常错误的代码编写习惯造成的。 正确的开发习惯是:将函数合理拆分组合,并为其合理的命名。 将一长串函数分割成多个独立的、可复用的、命名清晰的多个函数。

请相信我,正确的使用ramda会助你写出可读性更棒的代码。

避免使用 forEach

forEachfor看上去是等价的,不过我强烈建议你将forEach更换成for...of。 因为我认为forEach存在以下几个问题:

  1. forEach没有breakcontinue两个重要关键字。未来新需求加入时,有可能需要将其重构成for...of

    不能打断的遍历没有意思,就像是生气的女人,打断不了。 —— Anders Wang

  2. forEach无法很好的处理Promise。未来新需求需要添加异步逻辑时,我们需要将其重构成map或者是for...of
  3. forEach破坏了链式调用。map后可以接filterfilter后可以接map。但千万不要尝试在forEach后添加什么代码。

与其使用forEach,更换成for...of不是更好吗?

从我对forEach的态度可以看出来,我并不是一个纯粹的函数式支持者。 我认为优秀的代码,是满足基本需求之上具备最佳的可读性的代码。 在ramda中存在的ifElsewheneither等函数都将导致代码可读性变差,我一般是不会使用的。