Open lostvita opened 5 years ago
翻译: 安歌 原文: Functional Programming in JavaScript
翻译: 安歌
原文: Functional Programming in JavaScript
面向过程编程建立在过程调用的概念之上,包含了一系列的待执行的计算步骤。任何给定的过程都可以在程序执行期间被调用,包括其他或自身的程序。主要的面向过程的编程语言有COBOL、BASIC、C、ADA、GO。
面向对象编程建立在对象的概念之上,包含了属性和方法。这种模式更接近函数式编程。主要的面向对象的编程语言有C++、Java、PHP、C#、Python、Ruby、Swift等。
元编程把程序作为它们的数据,这意味着一个程序可以被设计成在运行时读取、生成、分析或转换其他程序,甚至对自己进行修改。
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
map()
在数学上,函数是一组输入和一组对应输出间的关系,它的特点是每一个输入组合都只有一个确切的输出。
在函数式编程中,这类函数称作纯函数,它只依赖与函数接收到的输入数据,除了返回结果外,不会改变输入的数据。
Math.random()不是纯函数,因为它的每一次调用都可能返回不同的值。
Math.random()
Math.min(1,2)是纯函数,因为对于给定的一组输入它总是返回相同的输出。
Math.min(1,2)
Array 函数
在上面的例子中,我们尝试只过滤出活跃的聚会组织,正如你看见的那样,我们能通过两种不同的方式来达到这个目的。第二种方式是函数式编程,filter()负责“程序如何执行”,我们只关注输入(meetups数组)以及输出(activeMeetupsFP),但在第一种方式中我们还需要关心如何使用for循环进行操作。
filter()
meetups
activeMeetupsFP
类似的,下面数组的其他方法,也有助于我们实现函数式编程并降低代码的复杂性:
它是一种调用多个方法的调用机制,通过函数返回一个对象的方式使得每一个方法的调用可以链接在一个语句中,而不需要临时变量存储中间结果值。 在上面的代码片段中,考虑到有10%的成员可能是重复的需要被除去,我们打印出所有活跃组织里的成员总和。
支持FP的库
这是一些提供了很多实用函数使得代码更具声明性的第三方库:
如果一个程序或表达式会修改其作用域之外的程序状态,或者除了返回值之外,对调用它的函数有任何其他可见的干扰,那么我们称其是有副作用的。 这个程序是有副作用的,因为函数scheduleMeetup的实际职责是给meetup对象添加date和place属性,但它却又修改了isActive的值,而这个属性是被其他函数如publishMeetup所依赖的。作为副作用,publishMeetup得不到期望的输出,因为它的输入在这期间已经发生了改变。在大型程序(真是程序)中,副作用是很难被调试的。
scheduleMeetu
date
place
isActive
publishMeetup
为确保函数不会更改原始数据而不是操作后返回数据的新副本,不变形显得尤为重要。
想象一下如果数组和对象在多个函数之间传递,而我们不去维护不变性,那么函数可能无法获取数组和对象的原始副本。
在易变对象和数组上发生错误是很难调试的。
易变性并不是没有用处,但应该在必要的时候才使用。
支持不变性的库
JavaScript默认不提供任何保证对象和数组不可变的限制,这里有一些库可以帮助我们实现不变性:
函数式编程的主要特征就是纯函数、不可变性和无副作用。
如果你喜欢这篇推文并觉得有帮助,请帮忙点个赞,谢谢!
什么是函数式编程
其他主流的编程范式
面向过程编程建立在过程调用的概念之上,包含了一系列的待执行的计算步骤。任何给定的过程都可以在程序执行期间被调用,包括其他或自身的程序。主要的面向过程的编程语言有COBOL、BASIC、C、ADA、GO。
面向对象编程建立在对象的概念之上,包含了属性和方法。这种模式更接近函数式编程。主要的面向对象的编程语言有C++、Java、PHP、C#、Python、Ruby、Swift等。
元编程把程序作为它们的数据,这意味着一个程序可以被设计成在运行时读取、生成、分析或转换其他程序,甚至对自己进行修改。
命令式模式 VS 声明式模式
Books
里面的每一个对象新增属性:lastReadBy
;for
循环,通过数组下标为每一个元素新增属性。因此,这种程序的焦点在于如何操作以达到期望的输出;map()
方法,这个方法接收一个函数作为参数并对容器中的每一个元素执行调用。这种代码描述的是要做什么而不是怎么做,实际的操作则由map()
负责。数学函数还是纯函数?
在数学上,函数是一组输入和一组对应输出间的关系,它的特点是每一个输入组合都只有一个确切的输出。
在函数式编程中,这类函数称作纯函数,它只依赖与函数接收到的输入数据,除了返回结果外,不会改变输入的数据。
Math.random()
不是纯函数,因为它的每一次调用都可能返回不同的值。Math.min(1,2)
是纯函数,因为对于给定的一组输入它总是返回相同的输出。为什么要函数式编程
函数式编程的例子
Array 函数
filter()
负责“程序如何执行”,我们只关注输入(meetups
数组)以及输出(activeMeetupsFP
),但在第一种方式中我们还需要关心如何使用for
循环进行操作。类似的,下面数组的其他方法,也有助于我们实现函数式编程并降低代码的复杂性:
函数链
它是一种调用多个方法的调用机制,通过函数返回一个对象的方式使得每一个方法的调用可以链接在一个语句中,而不需要临时变量存储中间结果值。
在上面的代码片段中,考虑到有10%的成员可能是重复的需要被除去,我们打印出所有活跃组织里的成员总和。
支持FP的库
这是一些提供了很多实用函数使得代码更具声明性的第三方库:
副作用
如果一个程序或表达式会修改其作用域之外的程序状态,或者除了返回值之外,对调用它的函数有任何其他可见的干扰,那么我们称其是有副作用的。
这个程序是有副作用的,因为函数
scheduleMeetu
p的实际职责是给meetup对象添加date
和place
属性,但它却又修改了isActive
的值,而这个属性是被其他函数如publishMeetup
所依赖的。作为副作用,publishMeetup
得不到期望的输出,因为它的输入在这期间已经发生了改变。在大型程序(真是程序)中,副作用是很难被调试的。不可变性
为确保函数不会更改原始数据而不是操作后返回数据的新副本,不变形显得尤为重要。
想象一下如果数组和对象在多个函数之间传递,而我们不去维护不变性,那么函数可能无法获取数组和对象的原始副本。
在易变对象和数组上发生错误是很难调试的。
易变性并不是没有用处,但应该在必要的时候才使用。
支持不变性的库
JavaScript默认不提供任何保证对象和数组不可变的限制,这里有一些库可以帮助我们实现不变性:
总结
函数式编程的主要特征就是纯函数、不可变性和无副作用。