# 函数式编程

## ppt内容

### 今日问题：为什么要使用函数式编程？

#### 1. 什么是函数式编程

* 一种编程范式，如何编程的方法论。一门语言一般会支持多种编程范式。
* 倡导利用若干简单的执行单元让计算结果不断渐进，逐层推导复杂的运算，而不是设计一个复杂的执行过程（主张声明式编程和编写抽象的代码）

例1：命令式vs声明式 ![image](http://t1.market.xiaomi.com/thumbnail/png/l1080/Duokan/019c854c34c3940a5083de29d8ce83139ba032244?) 例2：命令式的映射传入一个数字数组并返回一个每个数字都乘以2的新数组。 ![image](http://t1.market.xiaomi.com/thumbnail/png/l1080/Duokan/081ec4f56b7dc25dbaca065b16a506e24374225f1?)

从例子上可以看出，相比于命令式的代码，函数式的代码趋向于更简洁，且抽象部分具有更高的复用性。

若在更为复杂的业务需求中，声明式编程，还拥有更高的可预读性。（命名规范很重要！！！）

命令式的代码，则是站在计算机的角度，更贴近于计算机的执行过程。

#### 2. 函数式编程的好处

函数式编程的好处主要出自于以下特性

* 纯函数
* 柯里化
* 高阶函数

**2.1 函数式编程--纯函数**

* 纯函数：对于相同的输入，永远会得到相同的输出，而且没有任何可观察的副作用，也不依赖外部环境的状态的函数，叫做纯函数。
* 副作用：在计算结果的过程中，系统状态的一种变化，或者与外部世界进行的可观察的交互。比如修改了环境变量等。

例1:\
数组的一些工具方法，改变了传入数组的均为非纯函数；concat这类没有修改入参的，也没有引用或者修改外部变量的方法，是纯函数。 ![image](http://t1.market.xiaomi.com/thumbnail/png/l1080/Duokan/06db6c4c5c446483512b88601bc001cc49bd3a7d1?) 例2: ![image](http://t1.market.xiaomi.com/thumbnail/png/l1080/Duokan/01db649c544b6583d52b8c601ba006c748c41db1c?)

* 纯函数带来的好处
  * 测试无需考虑上下文，便于单元测试。因为纯函数的结果只与入参相关。
  * 纯函数相较于非纯函数也有着更好的复用性。
  * 纯函数适合写并发的执行代码。因为纯函数不会改变它的环境，这意味着我们根本不需要担心同步问题。
  * 在我们的项目中，站在组件的角度，组件化的开发与函数式编程的纯函数理念是相似的。

**2.2 函数式编程--柯里化（curry）**

所谓"柯里化"，就是把一个多参数的函数，转化为单参数函数。事实上柯里化也是一种闭包。 ![image](http://t1.market.xiaomi.com/thumbnail/png/l1080/Duokan/0613c55375acd4ca71a2ca16ffe5d9fa9d7e3ff80?)

**2.3函数式编程--高阶函数**

定义：接受另一个函数作为其参数的函数称为高阶函数(Higher-Order-Function)，或者说高阶函数是接受函数作为参数并且/或者返回函数作为输出的函数。一般而言，高阶函数通常用于抽象通用的问题，换句话说，高阶函数就是定义抽象。

节流（throtle）函数 ![image](http://t1.market.xiaomi.com/thumbnail/png/l1080/Duokan/0013c5d37ca7d7cae8a2cc16f6f5d6ff945418f95?)

### 总结

使用函数式编程的原因：

* 主要是我们的项目是组件化的，配合函数式编程的思想可以保证程序代码的健壮性，并提高组件的可复用性，降低开发成本。
* 看着高大上。。。

具体方案如下：

* 在编写函数的时候，尽量不修改全局变量和函数参数，保证函数的作用的内敛性，从而增加程序的健壮性。
* 简化我们的函数逻辑，每个函数专注一件事情，编写小而美的函数，采用函数嵌套来实现复杂的功能。提升可复用的粒度，提高代码可读性。
* 通过高阶函数抽象出通用的函数，提高代码的复用性，降低开发成本。

## 拓展内容

## 定义

"函数式编程"是一种"编程范式"（programming paradigm），也就是如何编写程序的方法论。它属于"结构化编程"的一种，相对于指令式编程更加强调程序执行的结果而非执行的过程，倡导利用若干简单的执行单元让计算结果不断渐进，逐层推导复杂的运算，而不是设计一个复杂的执行过程。简单来说，就是函数式编程关心类型（代数结构）之间的关系，命令式编程关心解决问题的步骤。

编程范式和编程语言之间的关系可能十分复杂，一个编程语言可以支持多种范式。 下面列举部分编程范式，作为参照：

* 指令式编程：是一种描述计算机所需作出的行为的编程典范。是面向计算机硬件的抽象，有变量（对应着存储单元），赋值语句（获取，存储指令），表达式（内存引用和算术运算）和控制语句（跳转指令），一句话，命令式程序就是一个冯诺依曼机的指令序列。
  * 计算机的硬件工作一般都是指令式的。现在的大部分编程语言都是指令式的，同时拓展支持其他的一些编程范式。早期的指令式编程语言都是计算机本身的机械语言，指示非常简单，令硬件的运行更容易，却阻碍了复杂程序的设计。
  * 优缺点：更贴近计算机执行逻辑，业务逻辑复杂时，可读性较低。
* 面向过程编程：是一种以过程为中心的编程典范。
  * 以什么正在发生为主要目标进行编程，注重发生事件的顺序，不同于面向对象的是谁在受影响。与面向对象明显的不同就是没有封装、继承、类。面向过程是从上往下步步求精，所以面向过程最重要的是模块化的思想方法。
  * 优缺点: 性能比面向对象高，因为类调用时需要实例化，开销比较大，比较消耗资源;面向过程的缺点：没有面向对象易维护、易复用、易扩展。
* 面向对象编程：是种具有对象概念的程序编程典范，同时也是一种程序开发的抽象方针。
  * 其本质是以建立模型体现出来的抽象思维过程和面向对象的方法。特点是具有重用性、灵活性和扩展性。
  * 面向对象的优点：易维护、易复用、易扩展，由于面向对象有封装、继承、多态性的特性，可以设计出低耦合的系统，使系统更加灵活、更加易于维护;面向对象的缺点：性能比面向过程低。

## Lambda表达式

* “Lambda 表达式”(lambda expression)是一个匿名函数，Lambda表达式基于数学中的λ演算得名，直接对应于其中的lambda抽象(lambda abstraction)，是一个匿名函数，即没有函数名的函数。Lambda表达式可以表示闭包（注意和数学传统意义上的不同）。

## 特性

* 闭包和高阶函数

  > * 函数编程支持函数作为第一类对象，有时称为闭包或者仿函数（functor）对象。实质上，闭包是起函数的作用并可以像对象一样操作的对象。与此类似，FP 语言支持高阶函数。高阶函数可以用另一个函数（间接地，用一个表达式） 作为其输入参数，在某些情况下，它甚至返回一个函数作为其输出参数。这两种结构结合在一起使得可以用优雅的方式进行模块化编程，这是使用 FP 的最大好处。
* 函数是“一等公民”

  > * 可赋值给变量
  > * 可作为参数传递
  > * 可作为返回值
  > * 可在执行时声明
  > * 可以匿名的形式存在
  > * 可立即执行
* 惰性计算

  > 在惰性计算中，表达式不是在绑定到变量时立即计算，而是在求值程序需要产生表达式的值时进行计算。
* 递归

  > 用递归做为控制流程的机制。目前js中的for循环，while循环等，都存在对变量的反复赋值，所以并不是一个纯函数式编程的实现。
* 只用"表达式"，不用"语句"

  > "表达式"（expression）是一个单纯的运算过程，总是有返回值；"语句"（statement）是执行某种操作，没有返回值。函数式编程要求，只使用表达式，不使用语句。也就是说，每一步都是单纯的运算，而且都有返回值。
* 没有"副作用"

  > 指的是函数内部与外部互动（最典型的情况，就是修改全局变量的值），产生运算以外的其他结果。
* 不修改状态

  > 函数式编程只是返回新的值，不修改系统变量。
* 引用透明性

  > 函数程序通常还加强引用透明性，即如果提供同样的输入，那么函数总是返回同样的结果。这有助于验证正确性、简化算法，甚至有助于找出优化它的方法。

## 纯函数

对于相同的输入，永远会得到相同的输出，而且没有任何可观察的副作用，也不依赖外部环境的状态的函数，叫做纯函数。

![](http://t1.market.xiaomi.com/thumbnail/png/l1080/Duokan/0be513473b591458815ada084cc59eb1f75f7b333?) ![](http://t1.market.xiaomi.com/thumbnail/png/l1080/Duokan/06e513573b571458615ad3084cc59eb2f75fdb333?) ![](http://t1.market.xiaomi.com/thumbnail/png/l1080/Duokan/03e5157737551658f25adb0841b59ab3fdb413ce3?)

## 优点

* 代码简洁，开发快速
* 接近自然语言，易于理解
* 更方便的代码管理
* 易于"并发编程"
* 代码的热升级

## 缺点

* 代码的简洁有时会带来额外的资源开销。
* 有些非函数式编程语言为求提升速度，不提供自动边界检查或自动垃圾回收等功能。
* 需要一定的学习成本。

## 总结

目前js开发中，完全的函数式编程是不符合实际的，但是我们可以借鉴它的思想。比如我们现在做的:

* 在编写函数的时候，尽量不修改全局变量和函数参数，保证函数的作用的内敛性，从而增加程序的健壮性。
* 比如简化我们的函数逻辑，编写小而美的函数，采用函数嵌套来实现复杂的功能。

  毕竟在复杂的业务里面，代码的正确性、可靠性、稳定性，直接影响到是否需要996。

## 参考

* [编程范式--wikipedia](https://zh.wikipedia.org/wiki/%E7%BC%96%E7%A8%8B%E8%8C%83%E5%9E%8B)
* [函数式编程--wikipedia](https://zh.wikipedia.org/wiki/%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B)
* [函数式编程--百度百科](https://baike.baidu.com/item/%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B)
* [Lambda表达式--百度百科](https://baike.baidu.com/item/Lambda%E8%A1%A8%E8%BE%BE%E5%BC%8F/4585794?fr=aladdin)
* [什么是函数式编程思维--知乎](https://www.zhihu.com/question/28292740/answer/40336090)
* [前端开发js函数式编程真实用途体现在哪里？--知乎](https://www.zhihu.com/question/59871249/answer/171201717)
* [你真的理解闭包和lambda表达式吗--简书](https://www.jianshu.com/p/c22db2a91989)
