什么是纯函数
纯函数是函数式编程中非常重要的一个概念,简单来说,就是一个函数的返回结果只依赖于它的参数,并且在执行过程中没有副作用,我们就把这个函数叫做纯函数
划重点:
- 函数的返回结果只依赖于它的参数
- 函数执行过程中没有副作用
首先来看第一点:函数的返回结果只依赖于它的参数
const a = 1
const impure = (b)=>a + b
impure(2) // 3
上面代码中, impure函数不是一个纯函数,因为它的返回结果依赖于外部变量a, 因为a是有可能变化的, 所以我们不能保证 impure(2)的值永远是3. 虽然impure函数的代码没有变化, 传入的参数也没有变化, 但它的返回值是不可预料的。 我们再来改一下:
const a = 1
const pure = (x, b) => x + b
pure(1,2) //3
现在, pure 满足纯函数的第一个条件,它的返回结果只依赖于它的参数 x 和 b, 就是说, 只要代码不变, pure(1,2)的返回值永远是3.
这就是纯函数的第一个条件: 函数的返回结果只依赖于它的参数
接下来解释第二点:函数执行中没有副作用
副作用指的是: 在计算结果的过程中,系统状态的一种变化, 或者与外部事件进行观察的交互。 再看一个例子:
var values = { a: 1 };
function impureFunction ( items ) {
var b = 1;
items.a = items.a * b + 2;
return items.a;
}
var c = impureFunction( values );
values.a // 3
在上面的代码中,我们改变了参数对象中的属性a, 由于我们定义的函数改变的对象在我们的函数作用域之外,导致这个函数称为“不纯”函数
var values = { a: 1 };
function pureFunction ( a ) {
var b = 1;
a = a * b + 2;
return a;
}
var c = pureFunction( values.a );
values.a // 1
上面的代码,我们只计算了作用域内的局部变量, 没有任何作用域外部的变量被改变, 因此这个函数是 “纯函数”
除了修改外部的变量, 一个函数在执行过程中还有很多方式产生外部可观察的变化,比如说调用 DOM API修改页面, 或者你发送了Ajax请求, 还有调用window.reload刷新浏览器, 甚至是console.log往控制台打印数据也是副作用。
总结一些副作用:
- 更改输入
- console.log
- HTTP请求(AJAX,fetch)
- 更改文件系统
- DOM查询
纯函数很严格, 也就是说你几乎除了计算数据以外什么都不能干, 计算的时候还不能依赖除了函数参数以外的数据。
我们常用的JS中的两个方法: splice和slice来举一个例子
var array1 = [0,1,2,3,4,5,6];
var array2 = [0,1,2,3,4,5,6];
var spliceArray = array1.splice(0,2);
var sliceArray = array2.slice(0,2);
console.log('array1: ' + array1);
console.log('spliceArray: ' + spliceArray);
console.log('array2: ' + array2);
console.log('sliceArray: ' + sliceArray);
运行结果:
array1: 2,3,4,5,6
spliceArray: 0,1
array2: 0,1,2,3,4,5,6
sliceArray: 0,1
可以看到, slice和splice的作用是大致相同的, 但是slice改变了原数组, 而slice却没有, 实际开发中, slice这种不改变原数组的方式更安全一些,改变原数组,是一种副作用
非纯函数带来的副作用
function getName(obj){
return obj.name;
}
function getAge(obj){
return obj.age;
}
function sayHi(person){
console.log('I am' + getName(person) + ',and I am' + getAge(person) + 'years old');
}
var Tom = { name: 'TOM', age: 26};
sayHi(Tom);
这里的sayHi不是纯函数, 它依赖于 getName, getAge两个函数, 如果我不小心改变了其中某个函数的功能, 这将使得sayHi中国函数出现错误。 当代码变得复杂,且由多人维护的时候,bug调试会变得非常复杂
使用纯函数的优点
1. 可复用性
纯函数仅依赖于传入的参数,这意味着你可以随意将这个函数移植到别的代码中, 只需要提供它需要的参数即可。 如果是非纯函数, 有可能你需要一根香蕉,却需要将整个香蕉树都搬过去
2. 可测试性
纯函数非常容易进行单元测试,因为不需要考虑上下文环境, 只需要考虑输入和输出
3. 并行代码
纯代码是健壮的, 改变执行次序不会对系统造成影响, 因此纯函数的操作可以并行执行。
总结
- 不产生副作用, 并且相同的输入总是返回相同的输出,那么这个函数是纯函数。
- 副作用包括但不限于: 更改输入,HTTP请求, 磁盘写入,打印
- 可以将函数输入拷贝一份用于更改,不要去动原来的数据。
- 扩展运算符语法(...语法)是一个复制对象或者数据的简便方法。
虽然纯函数有很多优点, 但也要避免滥用的情况,函数越纯,对环境依赖越小, 往往意味着要传入更多的参数。 我们的最终目的是: 让你的代码尽可能简单易懂和灵活。尽量在合适的场景使用它。