首页 > web前端 > js教程 > 功能组成:可维护代码的构建块

功能组成:可维护代码的构建块

Christopher Nolan
发布: 2025-02-17 11:19:09
原创
768 人浏览过

Function Composition: Building Blocks for Maintainable Code

JavaScript 函数组合:构建更易维护的代码

JavaScript 函数组合是一种将多个简单函数组合成单个更复杂函数的技术,这些函数按逻辑顺序对给定数据执行子函数操作。函数应用的顺序会产生不同的结果。

为了编写可维护的代码,理解每个被组合函数的返回类型至关重要,以确保下一个函数可以正确处理它。JavaScript 不会阻止组合返回不合适类型函数,因此这部分责任落在程序员身上。

对不熟悉函数式编程范式的人来说,组合函数可能会使代码看起来很复杂。但是,随着 ES2015 语法的出现,使用箭头函数,可以一行代码创建简单的组合函数,而无需特殊的组合方法。

组合函数应该是纯函数,这意味着每次将特定值传递给函数时,函数都应返回相同的结果,并且函数不应产生改变自身外部值的副作用。这将产生更简洁、更易读的代码。

本文由 Jeff Mott、Dan Prince 和 Sebastian Seitz 共同评审。感谢所有 SitePoint 的同行评审者,使 SitePoint 的内容达到最佳状态!

Function Composition: Building Blocks for Maintainable Code

函数式思维的优势之一是能够使用小型、易于理解的单个函数来构建复杂的功能。 但有时这需要反向思考问题,而不是正向思考,以便找出如何创建最优雅的解决方案。在本文中,我将采用循序渐进的方法来检查 JavaScript 中的函数组合,并演示它如何产生更易于理解且错误更少的代码。

嵌套函数

组合是一种技术,它允许你将两个或多个简单函数组合成一个更复杂的函数,该函数按逻辑顺序对传入的任何数据执行每个子函数。要获得此结果,你需要将一个函数嵌套在另一个函数中,并对内部函数的结果重复执行外部函数的操作,直到产生结果。并且结果可能因函数应用的顺序而异。这可以使用我们已经在 JavaScript 中熟悉的编程技术轻松演示,方法是将函数调用作为参数传递给另一个函数:

function addOne(x) {
  return x + 1;
}
function timesTwo(x) {
  return x * 2;
}
console.log(addOne(timesTwo(3))); //7
console.log(timesTwo(addOne(3))); //8
登录后复制
登录后复制
登录后复制
登录后复制

在这种情况下,我们定义了一个 addOne() 函数来为一个值加 1,以及一个 timesTwo() 函数将一个值乘以 2。通过将一个函数的结果作为另一个函数的参数传入,我们可以看到如何将其中一个嵌套在另一个中会产生不同的结果,即使初始值相同也是如此。内部函数先执行,然后结果传递给外部函数。

命令式组合

如果你想重复执行相同的操作序列,定义一个新函数来自动应用第一个函数和第二个函数可能会很方便。这可能看起来像这样:

function addOne(x) {
  return x + 1;
}
function timesTwo(x) {
  return x * 2;
}
console.log(addOne(timesTwo(3))); //7
console.log(timesTwo(addOne(3))); //8
登录后复制
登录后复制
登录后复制
登录后复制

在这种情况下,我们手动将这两个函数按特定顺序组合在一起。我们创建了一个新函数,该函数首先将传递的值分配给 holder 变量,然后通过执行第一个函数,然后是第二个函数来更新该变量的值,最后返回该 holder 的值。(请注意,我们使用名为 holder 的变量来临时保存我们传入的值。对于这样一个简单的函数,额外的局部变量似乎是多余的,但即使在命令式 JavaScript 中,将传入函数的参数值视为常量也是一个好习惯。局部修改它们是可能的,但这会造成关于在函数的不同阶段调用时参数值是什么的混淆。)类似地,如果我们想创建一个另一个新函数以相反的顺序应用这两个较小的函数,我们可以执行以下操作:

// ...来自上面的先前函数定义
function addOneTimesTwo(x) {
  var holder = x;
  holder = addOne(holder);
  holder = timesTwo(holder);
  return holder;
}
console.log(addOneTimesTwo(3)); //8
console.log(addOneTimesTwo(4)); //10
登录后复制
登录后复制

当然,这段代码开始看起来非常重复。我们的两个新的组合函数几乎完全相同,只是它们调用的两个较小函数的执行顺序不同。我们需要简化它(就像不要重复自己一样)。此外,使用像这样改变其值的临时变量并不是很函数式,即使它隐藏在我们正在创建的组合函数内部也是如此。底线:我们可以做得更好。

创建函数式组合

让我们创建一个组合函数,它可以采用现有的函数并将它们按我们想要的顺序组合在一起。为了以一致的方式做到这一点,而无需每次都处理内部细节,我们必须决定要将函数作为参数传入的顺序。我们有两个选择。参数将分别是函数,它们可以从左到右或从右到左执行。也就是说,使用我们提出的新函数,compose(timesTwo, addOne) 可以表示 timesTwo(addOne()) 从右到左读取参数,或者 addOne(timesTwo()) 从左到右读取参数。从左到右执行参数的优点是它们将以英语相同的阅读方式读取,这与我们命名组合函数 timesTwoAddOne() 以暗示乘法应在加法之前发生的方式非常相似。我们都知道逻辑命名对于简洁易读的代码的重要性。从左到右执行参数的缺点是待操作的值必须放在前面。但是,将值放在前面使得将来用其他函数组合结果函数不太方便。为了很好地解释这种逻辑背后的思想,你无法超越 Brian Lonsdorf 的经典视频 Hey Underscore, You’re Doing it Wrong。(尽管应该注意的是,现在 Underscore 有一个 fp 选项可以帮助解决 Brian 在将 Underscore 与函数式编程库(例如 lodash-fp 或 Ramda)一起使用时讨论的函数式编程问题。)无论如何,我们真正想要做的是首先传入所有配置数据,然后最后传入要操作的值。因此,将我们的组合函数定义为读取其参数并从右到左应用它们最合乎逻辑。因此,我们可以创建一个看起来像这样的基本组合函数:

function addOne(x) {
  return x + 1;
}
function timesTwo(x) {
  return x * 2;
}
console.log(addOne(timesTwo(3))); //7
console.log(timesTwo(addOne(3))); //8
登录后复制
登录后复制
登录后复制
登录后复制

使用这个非常简单的组合函数,我们可以更简单地构建我们之前的两个复杂函数,并看到结果相同:

// ...来自上面的先前函数定义
function addOneTimesTwo(x) {
  var holder = x;
  holder = addOne(holder);
  holder = timesTwo(holder);
  return holder;
}
console.log(addOneTimesTwo(3)); //8
console.log(addOneTimesTwo(4)); //10
登录后复制
登录后复制

虽然这个简单的组合函数有效,但它并没有考虑许多限制其灵活性和适用性的问题。例如,我们可能想要组合两个以上的函数。此外,我们在此过程中会丢失这些信息。我们可以解决这些问题,但为了掌握组合的工作原理,这不是必需的。与其自己编写,不如从现有的函数式库(例如 Ramda)继承更强大的组合,默认情况下,它确实考虑了参数从右到左的顺序。

类型是你的责任

重要的是要记住,程序员有责任知道每个被组合函数的返回类型,以便下一个函数可以正确处理它。与执行严格类型检查的纯函数式编程语言不同,JavaScript 不会阻止你尝试组合返回不合适类型值的函数。你不仅限于传递数字,甚至不限于从一个函数到下一个函数保持相同的变量类型。但是你负责确保你正在组合的函数已准备好处理前一个函数返回的任何值。

考虑你的受众

始终记住,其他人将来可能需要使用或修改你的代码。在传统的 JavaScript 代码中使用组合对于不熟悉函数式编程范式的人来说可能会显得复杂。目标是更简洁、更易于阅读和维护的代码。但是,随着 ES2015 语法的出现,甚至可以使用箭头函数将简单的组合函数作为单行调用来创建,而无需特殊的组合方法:

function addOne(x) {
  return x + 1;
}
function timesTwo(x) {
  return x * 2;
}
console.log(addOne(timesTwo(3))); //7
console.log(timesTwo(addOne(3))); //8
登录后复制
登录后复制
登录后复制
登录后复制

今天就开始组合吧

与所有函数式编程技术一样,重要的是要记住你的组合函数应该是纯函数。简而言之,这意味着每次将特定值传递给函数时,函数都应返回相同的结果,并且函数不应产生改变自身外部值的副作用。当您有一组想要应用于数据的相关功能,并且您可以将该功能的组件分解为可重用且易于组合的函数时,组合嵌套非常方便。与所有函数式编程技术一样,我建议谨慎地将组合添加到你的现有代码中以熟悉它。如果你做对了,结果将是更简洁、更干燥、更易读的代码。难道这不是我们都想要的吗?

(以下为FAQ,已根据原文进行调整和精简,并避免重复信息)

关于函数组合和可维护代码的常见问题

  • 什么是编程中的函数组合? 函数组合是将简单函数组合成更复杂函数的概念。你可以像搭积木一样,用这些小型可重用的函数创建复杂、强大的功能。这有助于使代码更易读、更易维护和更具可扩展性。

  • 函数组合如何促进可维护的代码? 函数组合通过鼓励使用小型、可重用的函数来促进可维护性。这些函数更容易理解、测试和调试。当这些小型函数组合在一起以创建更复杂的功能时,更容易查明和修复可能出现的任何问题。这种模块化方法还可以更容易地更新或修改代码的某些部分,而不会影响整个系统。

  • 编写可维护代码的最佳实践是什么? 编写可维护的代码涉及多项最佳实践,包括:保持函数小巧且专注于单一任务;使用有意义的变量和函数名称;对代码进行注释以解释代码逻辑背后的“原因”;遵循一致的编码风格和结构。

  • 函数组合与函数式编程有何关系? 函数组合是函数式编程中的一个基本概念。函数式编程是一种将计算视为数学函数的求值并避免更改状态和可变数据的范例。在这种范例中,函数组合在从更简单的函数构建更复杂的函数中起着至关重要的作用,从而提高了代码的可重用性和可维护性。

  • 实现函数组合的挑战是什么? 函数组合虽然有很多好处,但也可能带来挑战。一个挑战是,如果管理不当,它可能会导致难以阅读的代码,尤其是在组合多个函数时。在组合函数链中处理错误和异常也可能很困难。但是,可以通过良好的编码实践和正确使用函数组合来减轻这些挑战。

  • 函数组合如何改进代码测试? 函数组合可以使代码测试更轻松、更高效。由于组合函数是由较小、独立的函数组成的,因此你可以单独测试每个小型函数。这使得更容易隔离和修复错误。它还促进了单元测试的实践,其中软件的每个部分都单独进行测试以确保其正常工作。

以上是功能组成:可维护代码的构建块的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板