定义
实际上纯函数的定义更近似于数学上的函数的定义,数学上的函数定义如下:
设A,B是非空的数集,如果按照某种确定的对应关系f,使对于集合 A 中的任意一个数 x,在集合B中都有唯一确定的数 y 和它对应,那么就称 f:A->B 为从集合A 到集合B 的一个函数。
而纯函数的定义为:
纯函数定义为这样一种函数:对于相同的输入,其输出的结果永远是相同的,而且没有可见的副作用。
从定义上可以看出,纯函数和数学上的函数都有要求:对于任意相同输入,其输出结果都是 唯一确定 的,不存在一个输入对应于多个输出结果的情况。
纯函数和非纯函数很经常看到,比如数组的 slice
和 splice
函数,一个是纯函数,一个就是非纯函数:
slice
函数是纯函数,执行结果是可预计的、可靠的,每次输入相同的参数,执行操作的结果都一致:
var arr = [1, 2, 3, 4]; |
而 splice
函数不是纯函数,输入相同的参数,每次操作的结果都不一致:
// 非纯函数的示例: |
副作用
前面提到纯函数不存在副作用,副作用的影响是这样的:
副作用表示在运算过程中,系统状态的改变或者与外部进行的可观察的交互。如果一个函数与外部的可变状态发生了交互,则认为函数是有副作用的。
可以看个例子:
var minAge = 21; |
从以上示例可以看到,对于同一个输入,第一个函数存在副作用,这个函数的行为不光取决于函数的参数,而且依赖外部变量 minAge
,如果外部修改了 minAge
的值,函数的执行结果可能发生改变,这种改变是不可预期的,而第二个函数不存在副作用,没有使用或修改任何的外部变量,因此可以保证它的输出结果始终是不会发生改变的。
对于一个系统而言,依赖外部系统环境的状态会造成额外的复杂度,而使用纯函数可以有效降低系统的复杂度,而且还有一些其他的特性:
纯函数的特性
纯函数具有很多可靠的特性:
可缓存性
因为纯函数的输出结果是确定的,只和输入有关,因此对于复杂运算可以将结果存储起来,下次直接使用。
如下定义了一个非常耗时的操作:
var complexFunc = (val) => { |
这个操作执行较慢,每次都要花费大量时间,但是这是个纯函数,每次执行的结果是可预期的,因此,可以对其执行结果进行缓存:
//一个缓存函数结果的简单的实现 |
然后再次执行这个函数:
// 第一次执行未缓存 |
通过将执行结果缓存起来,虽然第一次调用的执行时间人就很长,但是当函数第二次被调用的时候,可以直接从缓存的数据中读取结果,函数的时间大大缩短。
可并行化
因为纯函数不需要访问共享数据,不会受外部状态影响而进行资源竞争,纯函数可以很容易的实行并行代码或者并行操作。