[译文]JavaScript函数式编程

本文介绍了函数式编程,它将计算视为对数学函数的求值,避免改变状态和易变数据。对比了其他主流编程范式,如面向过程、面向对象等,还区分了命令式与声明式模式。阐述了纯函数特点,说明了函数式编程的优势,列举了相关例子、函数链及支持库,最后强调其主要特征是纯函数、不可变性和无副作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

翻译: 安歌

原文: Functional Programming in JavaScript

什么是函数式编程

  • 在计算机科学中,函数式编程是一种编程范式
  • 函数式编程把计算看作是对数学函数的求值
  • 函数式编程避免了改变状态和易变数据

其他主流的编程范式

  • 面向过程编程
  • 面向对象编程
  • 元编程
  • 命令式编程
  • 声明式编程

面向过程编程建立在过程调用的概念之上,包含了一系列的待执行的计算步骤。任何给定的过程都可以在程序执行期间被调用,包括其他或自身的程序。主要的面向过程的编程语言有COBOL、BASIC、C、ADA、GO。

面向对象编程建立在对象的概念之上,包含了属性和方法。这种模式更接近函数式编程。主要的面向对象的编程语言有C++、Java、PHP、C#、Python、Ruby、Swift等。

元编程把程序作为它们的数据,这意味着一个程序可以被设计成在运行时读取、生成、分析或转换其他程序,甚至对自己进行修改。

命令式模式 VS 声明式模式

  • 命令式模式侧重于描述程序如何运行(怎么做),它由程序执行的命令组成;
  • 声明式模式侧重于描述程序要达成什么效果(做什么),并没有指定程序应该如何实现。
var books = [
  {name:'JavaScript', pages:450}, 
  {name:'Angular', pages:902},
  {name:'Node', pages:732}
];
/* 命令式编程 */
for (var i = 0; i < books.length; i++) {
  books[i].lastRead =  new Date();
}
/* 声明式编程 */
books.map((book)=> {
  book.lastReadBy = 'me';
  return book;
});
console.log(books);
复制代码
  • 在上面的代码片段中,我使用两种不同的方式给Books里面的每一个对象新增属性:lastReadBy
  • 第一种方式使用了for循环,通过数组下标为每一个元素新增属性。因此,这种程序的焦点在于如何操作以达到期望的输出;
  • 第二种方式使用了原生JavaScript里数组的map()方法,这个方法接收一个函数作为参数并对容器中的每一个元素执行调用。这种代码描述的是要做什么而不是怎么做,实际的操作则由map()负责。

数学函数还是纯函数?

在数学上,函数是一组输入和一组对应输出间的关系,它的特点是每一个输入组合都只有一个确切的输出。

在函数式编程中,这类函数称作纯函数,它只依赖与函数接收到的输入数据,除了返回结果外,不会改变输入的数据。

Math.random()不是纯函数,因为它的每一次调用都可能返回不同的值。

Math.min(1,2)是纯函数,因为对于给定的一组输入它总是返回相同的输出。

为什么要函数式编程

  • 纯函数特性保证了其作用域之外的变量不会被更改
  • 降低了复杂性,我们只需要关注程序要做什么而不是怎么做
  • 代码更易测试,结果更易验证,因为它不依赖程序的状态
  • 代码的可读性更强
  • 代码更易理解

函数式编程的例子

Array 函数

在上面的例子中,我们尝试只过滤出活跃的聚会组织,正如你看见的那样,我们能通过两种不同的方式来达到这个目的。第二种方式是函数式编程, filter()负责“程序如何执行”,我们只关注输入( meetups数组)以及输出( activeMeetupsFP),但在第一种方式中我们还需要关心如何使用 for循环进行操作。

类似的,下面数组的其他方法,也有助于我们实现函数式编程并降低代码的复杂性:

  • find
  • map
  • reduce
  • every
  • some

函数链

它是一种调用多个方法的调用机制,通过函数返回一个对象的方式使得每一个方法的调用可以链接在一个语句中,而不需要临时变量存储中间结果值。

在上面的代码片段中,考虑到有10%的成员可能是重复的需要被除去,我们打印出所有活跃组织里的成员总和。

支持FP的库

这是一些提供了很多实用函数使得代码更具声明性的第三方库:

  • RamdaJS
  • UndersoreJS
  • lodash

副作用

如果一个程序或表达式会修改其作用域之外的程序状态,或者除了返回值之外,对调用它的函数有任何其他可见的干扰,那么我们称其是有副作用的。

这个程序是有副作用的,因为函数 scheduleMeetup的实际职责是给meetup对象添加 dateplace属性,但它却又修改了 isActive的值,而这个属性是被其他函数如 publishMeetup所依赖的。作为副作用, publishMeetup得不到期望的输出,因为它的输入在这期间已经发生了改变。在大型程序(真是程序)中,副作用是很难被调试的。

不可变性

为确保函数不会更改原始数据而不是操作后返回数据的新副本,不变形显得尤为重要。

想象一下如果数组和对象在多个函数之间传递,而我们不去维护不变性,那么函数可能无法获取数组和对象的原始副本。

在易变对象和数组上发生错误是很难调试的。

易变性并不是没有用处,但应该在必要的时候才使用。

支持不变性的库

JavaScript默认不提供任何保证对象和数组不可变的限制,这里有一些库可以帮助我们实现不变性:

  • Seamess-immutable
  • ImmutableJs

总结

函数式编程的主要特征就是纯函数、不可变性和无副作用

如果你喜欢这篇推文并觉得有帮助,请帮忙点个赞,谢谢!

转载于:https://juejin.im/post/5d0898d4f265da1b86088979

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值