一、純函數
純函數是這樣一種函數,即相同的輸入,永遠會得到相同的輸出,而且沒有任何可觀察的副作用。
舉例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的溫牀。
概括來講,只要是跟函數外部環境發生的交互就都是副作用——這一點可能會讓你懷疑無副作用編程的可行性。函數式編程的哲學就是假定副作用是造成不正當行爲的主要原因。
這並不是說,要禁止使用一切副作用,而是說,要讓它們在可控的範圍內發生。副作用讓一個函數變得不純是有道理的:從定義上來說,純函數必須要能夠根據相同的輸入返回相同的輸出;如果函數需要跟外部事物打交道,那麼就無法保證這一點了。
參考書籍:函數式編程