纯函数

纯函数具有以下性质:

  • 输入确定,输出就确定
  • 仅取决于提供的输入,而不依赖于任何在函数求值期间或调用间隔时可能发生的隐藏状态和外部状态。
  • 不会造成超出其作用域的变化,例如修改全局对象或引用传递的参数

任何不符合以上条件的函数都是“不纯的”


不纯函数示例

我们来看下边这段代码:

  1. var counter = 0
  2. function increment() {
  3. return ++counter
  4. }
  5. console.log(increment()) // 1
  6. console.log(increment()) // 2

这个increment函数就是不纯的,因为他读取并修改了一个外部变量,即函数作用域外的counter。

一般来说,函数在读取或写入外部资源时都会产生副作用

我们希望达到输入确定,输出就确定的效果,但是每次调用increment()的时候,他的结果并不总是1,因为他引用的counter可能会被其他函数或重新赋值而改变。


纯函数示例

我们对上边示例代码进行修改,如下:

  1. var counter = 0;
  2. // function increment(num) {
  3. // return ++num
  4. // }
  5. const increment = num => ++num;
  6. console.log(increment(counter)) // 1
  7. console.log(increment(counter)) // 1
  8. console.log(increment(counter)) // 1

修改后的代码,只要我们传入的是0,输出结果就一定是1。这就是一个纯函数

纯函数优点

纯函数没有副作用-不受外界影响

典型的副作用操作:

  • 文件操作
  • DOM操作
  • HTTP请求
  • 查询系统状态
  • 用户交互
  • 数据库操作
  • 表单操作

纯函数的有点:

  • 可读性好
  • 并行运行
  • 引用透明
  • 代码可测
  • 易于模块化,易于复用

**

引用透明

如果一个函数对于相同的输入始终产出相同的结果,那么就说他是引用透明

另一种表现形式就是:函数可以用其返回值代替

  1. const plus = (a, b) => a + b
  2. const plus2 = () => plus(1, 2) + 3
  3. const plus3 = () => 3 + 3

上述代码中我们知道plus(1,2)的返回结果一定是3,那么我们可以在plus3里用3这个返回值代替plus2中的plus(1,2)函数。在这里plus函数就是引用透明的,也提现了一种可置换性
**

存储不可变数据

不可变数据是指:那些被创建后不能更改的数据。

我们来看一段代码:

  1. const sortDesc = function(arr) {
  2. return arr.sort(function(a, b) {
  3. return b - a
  4. })
  5. }
  6. const arr = [1,2,3,7,11]
  7. console.log(sortDesc(arr)) // [ 11, 7, 3, 2, 1 ]
  8. console.log(arr) // [ 11, 7, 3, 2, 1 ]

咋一眼看上去,这段代码开起来完全正常,并没有副作用。他确实如我们期望的那样,给一个数组,返回以降序排序的相同数组。

但是不幸的是,array.sort函数是有状态的,会导致在排序过程中产生副作用,因为原始引用arr也被修改了。后续章节我们会来克服它。

定义函数式编程


函数式编程是指:为创建不可变的程序,通过消除外部可见的副作用,来对纯函数的声明式的求值过程。**