目录

 

函数式编程的历史

函数的第一原则是要小,第二原则则是要更小 —— ROBERT C. MARTIN

解释一下上面那句话,就是我们常说的一个函数只做一件事,比如:将字符串首字母和尾字母都改成大写,我们此时应该编写两个函数。为什么呢?为了更好的复用,这样做保证了函数更加的颗粒化。

早在 1950 年代,随着 Lisp 语言的创建,函数式编程( Functional Programming,简称 FP)就已经开始出现在大家视野。而直到近些年,函数式以其优雅,简单的特点开始重新风靡整个编程界,主流语言在设计的时候无一例外都会更多的参考函数式特性( Lambda 表达式,原生支持 map ,reduce ……),Java8 开始支持函数式编程。

而在前端领域,我们同样能看到很多函数式编程的影子:Lodash.js、Ramda.js库的广泛使用,ES6 中加入了箭头函数,Redux 引入 Elm 思路降低 Flux 的复杂性,React16.6 开始推出 React.memo(),使得 pure functional components 成为可能,16.8 开始主推 Hooks,建议使用 pure functions 进行组件编写……

这些无一例外的说明,函数式编程这种古老的编程范式并没有随着岁月而褪去其光彩,反而愈加生机勃勃。

什么是函数式编程

上面我们了解了函数式编程的历史,确定它是个很棒的东西。接下来,我们要去了解一下什么是函数式编程?

其实函数我们从小就学,什么一元函数(f(x) = 3x),二元函数……根据学术上函数的定义,函数即是一种描述集合和集合之间的转换关系,输入通过函数都会返回有且只有一个输出值。

所以,函数实际上是一个关系,或者说是一种映射,而这种映射关系是可以组合的,一旦我们知道一个函数的输出类型可以匹配另一个函数的输入,那他们就可以进行组合。

在编程的世界里,我们需要处理其实也只有“数据”和“关系”,而“关系”就是函数,“数据”就是要传入的实参。我们所谓的编程工作也不过就是在找一种映射关系,比如:将字符串首字母转为大写。一旦关系找到了,问题就解决了,剩下的事情,就是让数据流过这种关系,然后转换成另一个数据返回给我们。

想象一个流水线车间的工作过程,把输入当做原料,把输出当做产品,数据可以不断的从一个函数的输出可以流入另一个函数输入,最后再输出结果,这不就是一套流水线嘛?

所以,现在你明确了函数式编程是什么了吧?它其实就是强调在编程过程中把更多的关注点放在如何去构建关系。通过构建一条高效的建流水线,一次解决所有问题。而不是把精力分散在不同的加工厂中来回奔波传递数据。

函数式编程的特点

  • 函数是一等公民

根据维基百科,编程语言中一等公民的概念是由英国计算机学家Christopher Strachey提出来的,时间则早在上个世纪60年代,那个时候还没有个人电脑,没有互联网,没有浏览器,也没有JavaScript。并且当时也没给出清晰的定义。

关于一等公民,我找到一个权威的定义,来自于一本书《Programming Language Pragmatics》,这本书是很多大学的程序语言设计的教材。

In general, a value in a programming language is said to have first-class status if it can be passed as a parameter, returned from a subroutine, or assigned into a variable.

也就是说,在编程语言中,一等公民可以作为函数参数,可以作为函数返回值,也可以赋值给变量。

例如,字符串在几乎所有编程语言中都是一等公民,字符串可以做为函数参数,字符串可以作为函数返回值,字符串也可以赋值给变量。

对于各种编程语言来说,函数就不一定是一等公民了,比如Java 8之前的版本。

对于JavaScript来说,函数可以赋值给变量,也可以作为函数参数,还可以作为函数返回值,因此JavaScript中函数是一等公民。

  • 声明式编程 (Declarative Programming)

通过上面的例子可以看出来,函数式编程大多时候都是在声明我需要做什么,而非怎么去做。这种编程风格称为声明式编程 

// 比如:我们要打印数组中的每个元素 // 1. 命令式编程 let arr = [1, 2, 3]; for (let i = 0, len = arr.length; i < len; i++) {   console.log(arr[i]) }  // 2. 声明式编程 let arr = [1, 2, 3]; arr.forEach(item => {   console.log(item) })  /* * 相对于命令式编程的 for 循环拿到每个元素,声明式编程不需要自己去找每个元素 * 因为 forEach 已经帮我们拿到了,就是