3. 纯函数
<blockquote>
<p>纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。</p>
</blockquote>
<p>比如 slice 和 splice,这两个函数的作用并无二致——但是注意,它们各自的方式却大不同,但不管怎么说作用还是一样的。我们说 slice 符合纯函数的定义是因为对相同的输入它保证能返回相同的输出。而 splice 却会嚼烂调用它的那个数组,然后再吐出来;这就会产生可观察到的副作用,即这个数组永久地改变了。</p>
<pre><code class="language-javascript">var xs = [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]
// 不纯的
xs.splice(0,3);
//=> [1,2,3]
xs.splice(0,3);
//=> [4,5]
xs.splice(0,3);
//=> []</code></pre>
<p>在函数式编程中,我们讨厌这种会改变数据的笨函数。我们追求的是那种可靠的,每次都能返回同样结果的函数,而不是像 splice 这样每次调用后都把数据弄得一团糟的函数,这不是我们想要的。
来看看另一个例子。</p>
<pre><code class="language-javascript">// 不纯的
var minimum = 21;
var checkAge = function(age) {
return age >= minimum;
};
// 纯的
var checkAge = function(age) {
var minimum = 21;
return age >= minimum;
};
</code></pre>
<p>副作用让一个函数变得不纯是有道理的:从定义上来说,纯函数必须要能够根据相同的输入返回相同的输出;如果函数需要跟外部事物打交道,那么就无法保证这一点了。
我们来仔细了解下为何要坚持这种「相同输入得到相同输出」原则。</p>
<h4>追求“纯”的理由</h4>
<p>首先,纯函数总能够根据输入来做缓存。实现缓存的一种典型方式是 memoize 技术:</p>
<pre><code class="language-javascript">//缓存代理模式
//例如 : f(1,2,3) 必要的时候以空间换时间
//f(1,2,3)
//主要形成 cache {
// 1,23: 24
//}
/**************** 计算乘积 *****************/
var mult = function(){
var a = 1;
for ( var i = 0, l = arguments.length; i < l; i++ ){
a = a * arguments[i];
}
return a;
};
/**************** 计算加和 *****************/
var plus = function(){
var a = 0;
for ( var i = 0, l = arguments.length; i < l; i++ ){
a = a + arguments[i];
}
return a;
};
/**************** 创建缓存代理的工厂 *****************/
var createProxyFactory = function( fn ){
var cache = {};
return function(){
var args = Array.prototype.join.call( arguments, ',' );
if ( args in cache ){
console.log(cache)
return cache[ args ];
}
return cache[ args ] = fn.apply( this, arguments );
}
};
var proxyMult = createProxyFactory( mult ), proxyPlus = createProxyFactory( plus );
console.log ( proxyMult( 1, 2, 3, 4 ) ); // 输出:24
console.log ( proxyMult( 1, 2, 3, 4 ) ); // 输出:24
console.log ( proxyPlus( 1, 2, 3, 4 ) ); // 输出:10
console.log ( proxyPlus( 1, 2, 3, 4 ) ); // 输出:10
console.log ( proxyPlus( 1, 2, 3, 4 ) ); // 输出:10
</code></pre>