JavaScript 非常适合函数式编程的特点之一是它可以接受高阶函数。高阶函数是一个可以将另一个函数作为参数的函数,或者返回一个函数作为结果的函数。
一等函数:JavaScript 中作为对象的函数
您可能听说过 JavaScript 将函数视为一等公民。这意味着 JavaScript 中的函数被视为对象。它们具有 type Object,可以作为变量的值赋值,并且可以像任何其他引用变量一样传递和返回。
这种原生能力赋予了 JavaScript 在函数式编程方面的特殊能力。因为函数是对象,所以该语言支持一种非常自然的函数式编程方法。事实上,它是如此自然,我敢打赌你一直在使用它,甚至没有考虑它。
高阶函数如何将函数作为参数
如果您做过很多基于 Web 的 JavaScript 编程或前端开发,您可能会遇到使用回调的函数。回调是在所有其他操作完成后在操作结束时执行的函数。通常这个回调函数作为函数的最后一个参数传入。通常,它被内联定义为匿名函数。
由于 JavaScript 是单线程的,这意味着一次只发生一个操作,因此将要发生的每个操作都沿着这个单线程排队。在父函数的其余操作完成后传入要执行的函数的策略是支持高阶函数的语言的基本特征之一。它允许异步行为,因此脚本可以在等待结果的同时继续执行。在处理可能在未确定的时间段后返回结果的资源时,传递回调函数的能力至关重要。
这在 Web 编程环境中非常有用,其中脚本可能会将 Ajax 请求发送到服务器,然后需要在响应到达时处理响应,而事先不知道服务器上的网络延迟或处理时间。Node.js 经常使用回调来最有效地利用服务器资源。这种方法在应用程序在执行功能之前等待用户输入的情况下也很有用。
例如,考虑这个简单的 JavaScript 片段,它向按钮添加了一个事件侦听器。
此脚本使用匿名内联函数来显示警报。但它可以很容易地使用单独定义的函数,并将该命名函数传递给addEventListener方法
请注意,我们传递了proveIt而不是传递proveIt()给我们的addEventListener函数。当您按名称传递不带括号的函数时,您传递的是函数对象本身。当你用括号传递它时,你传递的是执行该函数的结果。
我们的小proveIt()函数在结构上独立于它周围的代码,总是返回id被触发的任何元素的。这段代码可以存在于您希望id使用元素显示警报的任何上下文中,并且可以使用任何事件侦听器调用。
用单独定义和命名的函数替换内联函数的能力开辟了一个充满可能性的世界。当我们尝试开发不改变外部数据的纯函数,并且每次都为相同的输入返回相同的结果时,我们现在拥有一个基本工具来帮助我们开发一个可以使用的小型、有针对性的函数库一般在任何应用程序中。
免费学习领取JAVA 课件,源码,安装包等关注与私信博主
使用高阶函数将函数作为结果返回
除了将函数作为参数之外,JavaScript 还允许函数返回其他函数作为结果。这是完全合理的,因为函数只是对象,它们可以像任何其他值一样返回。
但是作为结果返回一个函数是什么意思呢?将一个函数定义为另一个函数的返回值允许您创建可用作模板来创建新函数的函数。这打开了通向另一个函数式 JavaScript 魔法世界的大门。
例如,假设您已经厌倦了阅读所有这些关于千禧一代的特殊性的文章,并且您决定每次出现时都将千禧一代一词替换为短语Snake People 。您的冲动可能只是编写一个函数,对您传递给它的任何文本执行文本替换:
这行得通,但它非常特定于这种情况。你也厌倦了听到婴儿潮一代的消息。您还想为它们创建一个自定义函数。但即使使用如此简单的函数,您也不想重复您编写的代码:
但是,如果您决定做一些更有趣的事情来保留原始字符串中的大小写呢?您必须修改您的两个新功能才能做到这一点。这很麻烦,而且会使您的代码更脆弱,更难阅读。
您真正想要的是能够在模板函数中将任何术语替换为任何其他术语的灵活性,并将该行为定义为基础函数,您可以从中生成一系列自定义函数。
由于能够返回函数而不是值,JavaScript 提供了使该场景更加方便的方法:
我们所做的是将执行实际工作的代码隔离到一个通用且可扩展的attitude函数中,该函数封装了使用原始短语和带有某种态度的替换短语来修改任何输入字符串所需的所有工作。
将新函数定义为对该attitude函数的引用,并预先填充它所采用的前两个参数,从而允许新函数接受您传递给它的任何参数并将其用作函数返回的内部函数中的源文本attitude。
我们在这里利用了这样一个事实,即 JavaScript 函数不关心它们是否传递了与最初定义的参数数量相同的参数。如果缺少参数,该函数只会将缺少的参数视为未定义。
另一方面,当被调用的函数以我们刚刚演示的方式定义时,可以稍后传入额外的参数,作为对从另一个函数返回的函数的引用,该函数的参数(或更多)未定义。
如果需要,请重复几次,以便您完全了解正在发生的事情。我们正在创建一个返回另一个函数的模板函数。然后我们将新返回的函数定义为模板函数的自定义实现,减去一个属性。以这种方式创建的所有函数都将从模板函数继承相同的工作代码,但可以使用不同的默认参数进行预定义。
你已经在使用高阶函数
高阶函数对于 JavaScript 的工作方式是如此基础,你已经在使用它们了。每次传递匿名函数或回调时,实际上都是在获取传递函数返回的值,并将其用作另一个函数的参数(例如使用箭头函数)。
函数返回其他函数的能力扩展了 JavaScript 的便利性,允许我们创建自定义命名的函数来使用共享模板代码执行专门的任务。这些小功能中的每一个都可以继承原始代码中的任何改进,这有助于我们避免代码重复,并保持源代码的清洁和可读性。
作为一个额外的好处,如果你确保你的函数是纯的,这样它们不会改变外部值并且它们总是为任何给定的输入返回相同的值,你就可以很好地创建测试套件来验证你的代码更新模板函数时,更改不会破坏您所依赖的任何内容。
开始思考如何在自己的项目中利用这种方法。JavaScript 的一大优点是您可以将函数式技术与您已经熟悉的代码混合在一起。尝试一些实验。您可能会惊讶于使用高阶函数可以轻松地改进您的代码。
免费学习领取JAVA 课件,源码,安装包等关注与私信博主学习资料