在函数式JavaScript中使用Map和Reduce

大卫·格林
分享

说了这么多支持ECMAScript 6中令人惊叹的新特性的工作流,我们很容易忘记ECMAScript 5为我们带来了一些支持JavaScript函数式编程的很棒的工具。这些人中有本地人map ()而且reduce ()方法数组对象。

如果你不吸毒的话map ()而且reduce ()今天,你该开始了。大多数当代JavaScript平台都支持ECMAScript 5。映射和缩减可以使您的代码更干净,更易于阅读和维护,并使您走上更优雅的功能开发之路。

性能:警告

当然,当情况需要时,阅读和维护代码必须与性能相平衡。目前浏览器可以更有效地执行使用更麻烦的传统技术,例如for循环。

我的方法通常是编写代码首先考虑可读性和可维护性,然后如果我在现实世界中注意到问题,就会优化性能。过早的优化是魔鬼。

也值得考虑使用方法,如map ()而且reduce ()可以更好地利用JavaScript引擎的改进,因为将来浏览器会为它们进行优化。除非我在性能问题上无路可走,否则我更喜欢乐观地编码,并把使我的代码不那么有吸引力的性能调整放在我的口袋里,以备不时之需。

使用地图

映射是一种基本的函数式编程技术,用于操作数组中的所有元素,并生成具有转换后内容的相同长度的另一个数组。

为了更具体一点,让我们提出一个简单的用例。例如,假设您有一个单词数组,您需要将其转换为一个包含每个单词长度的数组。(我知道,对于复杂的应用程序,这不是您经常需要做的那种复杂的火箭科学,但是理解它在这样一个简单的情况下是如何工作的,将有助于您将它应用于可以为代码增加真正价值的情况)。

您可能已经知道如何使用在数组上循环。它可能看起来像这样:

var动物“猫”“狗”“鱼”var长度varvarvar循环动物长度0<循环++动物长度长度控制台日志长度//[3,3,4]

我们所做的只是定义了几个变量:一个名为动物它包含了我们的单词,一个名为长度它将包含操作的输出,以及名为来临时存储我们要在数组的每个循环中操作的每个项。我们建立了一个带有临时内部的循环变量和a循环变量来优化循环。然后我们遍历每一项,直到动物数组中。对于每一个,我们计算长度,并把它推到长度数组中。

注意:可以说,我们可以通过推入of的长度而不使用item变量来更简洁地完成此操作动物(计数)直接到长度没有中间赋值的数组。这样可以节省一些代码,但也会降低可读性,即使对于这个非常简单的示例也是如此。类似地,为了使这个更有性能但不那么直接,我们可以使用的已知长度动物数组来初始化长度数组作为新数组(animals.length),然后按索引插入项目,而不是使用push。这完全取决于您将如何在现实世界中使用这些代码。

这种方法在技术上没有任何问题。它应该可以在任何标准的JavaScript引擎中工作,并且它将完成工作。但一旦你知道如何使用map ()这样做看起来很笨拙。

让我来告诉你如何使用这个map ()

var动物“猫”“狗”“鱼”var长度动物地图函数动物返回动物长度控制台日志长度//[3,3,4]

在本例中,我们再次从变量开始动物动物类型的数组。然而,我们声明的另一个变量是长度类的每个元素上映射一个匿名内联函数后,我们将其值直接赋给结果动物数组中。该匿名函数对每个动物执行一个操作,返回长度。结果,长度变成与原始数组长度相同的数组动物数组,包含每个单词的长度。

关于这种方法,有几点需要注意。首先,它比原版短得多。其次,我们必须声明更少的变量。更少的变量意味着全局名称空间中的噪音更少,如果相同代码的其他部分使用具有相同名称的变量,则冲突的机会更少。此外,我们没有一个变量需要从头到尾改变它们的值。随着对函数式编程的深入,您将体会到使用常量和的强大功能不变的变量而且开始永远都不嫌早。

这种方法的另一个优点是,我们有机会通过分离命名函数来提高其通用性,从而在过程中产生更清晰的代码。匿名内联函数看起来很混乱,并且使代码更难重用。我们可以定义一个命名的getLength ()函数,并在上下文中这样使用它:

var动物“猫”“狗”“鱼”函数getLength返回长度控制台日志动物地图getLength//[3,3,4]

看到它多干净了吗?仅仅将映射作为工具包的一部分就可以将代码提升到一个全新的功能级别。

什么是函子?

值得注意的是,通过向数组对象添加映射,ECMAScript 5将基本数组类型转换为完整的函子,使函数式编程对我们所有人来说都更容易访问。

根据经典函数式编程定义,函子满足三个标准:

  1. 它包含一组值
  2. 它实现了一个映射函数来操作每个元素
  3. 它的map函数返回一个相同大小的函子

这将是您在下次JavaScript Meetup上讨论的内容。

如果你想了解更多关于函子的知识,请点击查看这个视频很棒作者:Mattias Petter Johansson

使用减少

reduce ()方法也是ECMAScript 5中的新方法,它类似于map (),只不过没有产生另一个函子,reduce ()生成可以是任何类型的单个结果。例如,想象一下,你想要得到文本中所有单词的长度之和动物数组作为一个数字。你可以这样开始:

var动物“猫”“狗”“鱼”var总计0varvar0循环动物长度<循环++动物总计+ =长度控制台日志总计/ / 10

定义初始数组后,创建一个变量总计对于运行总数,初始设置为0。我们还创建了一个变量来进行每次迭代动物数组通过循环,和一个变量对于循环计数器,以及循环变量来优化我们的迭代。然后运行中的所有单词进行循环动物数组,将每一个赋值给变量。最后,我们把每一项的长度加到总数中。

同样,这种方法在技术上没有任何问题。我们从一个数组开始,最后得到一个结果。但是有了reduce ()方法,我们可以让它更直接:

var动物“猫”“狗”“鱼”var总计动物减少函数总和返回总和+长度0控制台日志总计

这里我们定义了一个新变量,总计,并将化简的结果赋给它动物数组使用两个参数:匿名内联函数和初始运行值为零。reduce遍历数组中的每个项,对该项执行函数,并将其添加到传递给下一次迭代的运行总数中。在这里,我们的内嵌函数接受两个参数:运行的和和,以及当前正在从数组中处理的单词。的当前值总计到当前单词的长度。

注意,我们设置了的第二个参数reduce ()到0,这就确定了总计将包含一个数字。如果没有第二个参数,reduce方法仍然可以工作,但结果不一定是您所期望的。(请尝试一下,看看是否可以推导出JavaScript在省略总计时使用的逻辑。)

方法调用时对内联函数进行了集成定义,因此这看起来可能比实际需要的复杂一些reduce ()方法。让我们再做一次,但让我们先定义一个命名函数,而不是使用匿名的内联函数:

var动物“猫”“狗”“鱼”varaddLength函数总和返回总和+长度var总计动物减少addLength0控制台日志总计

这有点长,但长并不总是坏事。这样看会让你更清楚reduce方法发生了什么。

reduce ()方法接受两个参数:应用于数组中的每个元素的函数和用于运行总数的初始值。在本例中,我们传递了一个名为addLength以及运行总数的初始值为0。我们已经创建了addLength ()函数,以便它也接受两个参数:一个运行的和和一个要处理的字符串。

结论

习惯使用map ()而且reduce ()将为您提供替代方案,使您的代码更干净、更通用、更可维护,并为您使用更实用的JavaScript技术铺平道路。

map ()而且reduce ()方法是添加到ECMAScript 5的新方法中的两个。很有可能,您今天使用它们所看到的代码质量和开发人员满意度的改进将远远超过对性能的临时影响。使用功能技术进行开发,并在决定是否使用功能技术之前测量其在现实世界中的影响map ()而且reduce ()适合你的应用。

本文由Panayiotis Velisarakos蒂姆Severien而且丹王子。感谢所有SitePoint的同行审必威西盟体育网页登录稿人,让SitePoint的内容成为最好的!

Baidu