什么是函数式编程,函数式编程能为我们解决什么问题?
一、定义
函数式编程 是一种 编程范式,是编程的一种方法论。其中常见的编程范式有 命令式编程、函数式编程和逻辑式编程。
命令式编程 是对 计算机硬件的一种抽象,拥有 变量(存储单元),表达式(内存引用、数学运算)和控制语句(跳转指令)。通过将达到目的的步骤详情的描述出来,交给程序处理。即命令式程序就是一个冯诺依曼机的 指令序列。
而 函数式编程 则是对数学的抽象,将运算过程描述为一种 表达式求值。
简单来说就是,命令式编程关心解决问题的步骤,而函数式编程关心数据的映射 (该映射规则只关心输入,相同的输入总是获得一致的输出)。
举个例子对比一下:
1 | // demo 计算 1 + 2 - 3 + 4 |
为了避免代码过于 横向拓展,陷入“回调地狱”,建议进行 链式优化 或 Promise模式。
1 | // loadash 链式写法 |
1. 函数式编程的本质:
- 函数是一等公民(函数与其他数据类型一样,处于平等地位,能进行赋值和传参)
- 引用透明 & 没有副作用(函数的运行不依赖外部状态,也不会修改外部状态)
- 不可变性(函数保持独立,不会对输入参数进行变更,只返回新的值)
函数式编程具有的好处有:
- 语义更加清晰
- 可复用性更高
- 可维护性好(函数保持独立,没有副作用,每一个函数都可视作一个单元,进行测试和调试)
- 易于“并发”,不会造成资源争用(不依赖外部状态)
不过由于函数参数的不可变性,纯函数编程语言是无法实现循环的,只能通过递归的方式解决迭代问题,这使得函数式编程严重依赖递归。
1 | // 阶乘 |
这样的递归调用有更高的开销和局限(调用栈深度),可以尽量把递归写成尾递归的方式,编译器会自动优化为循环。
二、常见函数式编程模型
1. 高阶函数(Higher-order function)
接受或返回一个函数的函数被称为高阶函数
1 | const arr = [1, 2, 3]; |
2. 闭包(Closure)
可以保留局部变量不被释放的代码块,被称为一个闭包。闭包存在内、外两层函数,同时内层函数对外层函数的局部变量进行了引用。
例如斐波那契数列,可利用闭包缓存运算结果:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 创建闭包
function fibonacciSequence() {
const data = [1, 1];
const sequence = n => {
if (!data[n]) {
data[n] = sequence(n - 1) + sequence(n - 2);
}
return data[n];
}
return sequence;
}
const sequence = fibonacciSequence();
console.log(sequence(6)); // 13
console.log(sequence(5)); // 8
闭包的主要用于持久化变量,并利用这些变量做缓存或者持久化变量。但是持久化变量会持续占用内存空间,易造成内存浪费,一般需要进行额外的手动清除。
3. 柯里化(Currying)
给定一个函数的部分参数,生成一个接受其他参数的新函数,即为柯里化。
1 | // 获取相对于 BASE 的路径 |
本例中 aPath
和 bPath
不用再关心 BASE
路径。柯里化可以使我们只关心函数的部分参数,使函数的用途更加清晰,调用更加简单。
4. 组合(Composing)
将多个函数的能力合并,创造一个新的函数,便是组合。
1 | // 数组中每个单词大写,做 Base64 |
_.flow 将转大写和转 Base64 的函数的能力合并,生成一个新的函数。方便作为参数函数或后续复用。
5. 函数式组件
一个函数就是一个组件,其入参为渲染的上下文,返回值则是渲染好的 HTML。
对于函数式组件而言,其特点有:
Stateless
,组件自身没有状态Instanceless
,组件自身也没有实例,即this
。- 无生命周期
当组件不涉及内部状态,只是用于数据渲染时,函数式组件更轻量,性能更好。具体内容可参考
React
和Vue
的函数式组件。
三、自己眼中的函数式编程
我理解的函数式编程,是 以函数为载体,进行数据的映射处理,其独立而不受外界影响。虽然函数式编程有很多优点和场景,但是在开发过程中,我们不应该拘泥于这一种编程思维,而应该多对比思考其他思想,甚至多种结合进行设置。
此处警醒自己不要钻牛角尖