一、纯函数
纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。
举例1:数组的两个方法:slice与splice。
这两个函数的作用并无二致——但是注意,它们各自的方式却大不相同,但不管怎么说作用还是一样的。我们说 slice 符合纯函数的定义是因为对相同的输入它保证能返回相同的输出。而 splice 却会嚼烂调用它的那个数组,然后再吐出来;这就会产生可观察到的副作用,即这个数组永久地改变了。
var xs = [1,2,3,4,5];
var xp = [1,2,3,4,5];
//纯的
xs.slice(0,3); //=>[1,2,3]
xs.slice(0,3); //=>[1,2,3]
xs.slice(0,3); //=>[1,2,3]
console.log(xs); //=>[1,2,3,4,5];
//不纯的
xp.splice(0,3); //=>[1,2,3]
xp.splice(0,3); //=>[4,5]
xp.splice(0,3); //=>[]
console.log(xp); //=>[];
在函数式编程中,我们讨厌这种会改变数据的笨函数。我们追求的是那种可靠的,每次都能返回同样结果的函数,而不是像 splice这样每次调用后都把数据弄得一团糟的函数,这不是我们想要的。
举例2:
//不纯的
var minimum = 21;
var checkAge = function (age) {
return age >= minimum;
};
//纯的
var checkAge = function (age) {
var minimum = 21;
return age >= minimum;
};
在不纯的版本中,checkAge的结果将取决于minimum这个可变变量的值。换句话说,全局作用域下的minimum改变之后,会影响checkAge的结果。在纯的版本中,minimum值被限定在函数作用域中,外部无法直接修改,所以函数结果只会和参数age有关。
二、副作用
副作用是在计算结果的过程中,系统状态的一种变化,或者与外部世界进行的可观察的交互。
“作用”我们可以理解为一切除结果计算之外发生的事情。
“副作用”的关键部分在于“副”。就像一潭死水中的“水”本身并不是幼虫的培养器,“死”才是生成虫群的原因。同理,副作用中的“副”是滋生bug的温床。
概括来讲,只要是跟函数外部环境发生的交互就都是副作用——这一点可能会让你怀疑无副作用编程的可行性。函数式编程的哲学就是假定副作用是造成不正当行为的主要原因。
这并不是说,要禁止使用一切副作用,而是说,要让它们在可控的范围内发生。副作用让一个函数变得不纯是有道理的:从定义上来说,纯函数必须要能够根据相同的输入返回相同的输出;如果函数需要跟外部事物打交道,那么就无法保证这一点了。
参考书籍:函数式编程