Pure Function
A pure function is a function that depends only on its declared inputs and its internal algorithm to produce its output. It does not read any other values from “the outside world” — the world outside of the function’s scope — and it does not modify any values in the outside world. — alvinalexander.com
Pure Function的兩個重點:
(1) output 只跟 input有關
(2) 沒有附作用
PF (Pure Funciton)
= ODI (Output Depends on Input)
+ NSE (No Side Effects)
看下面的例子吧:
function sum(a, b) {
const result = a + b;
return result;
}const x = 1;
const y = 2;const answer = sum(x, y); // answer is 3;
有一個名稱為 sum 的function,return的值只來自參數的 a 跟 b (Output Depends on Input)。執行sum之後,得到新的變數為 3,而且原本的x 和y沒有改變 (No Side Effects)。這個 sum 就是 Pure Function。
再舉個例子,從反例開始:
假設有一組資料陣列 list :
const list = [
{ id:'a1', name:'apple', qty:2 }
{ id:'b2', name:'banana', qty:3 }
];
現在要在list中新增一筆資料:
const item = { id:'c3', name:'cherry', qty:0 }
讓結果的 list應該長這樣:
newlist = [
{ id:'a1', name:'apple', qty:2 },
{ id:'b2', name:'banana', qty:3 },
{ id:'c3', name:'cherry', qty:0 }
]
期望實作一個 funciton 叫 addItem ,可以滿足上述的需求:
// 反例(1)const addItem = (item) => {
list.push(item);
return list;
}const newlist = addItem(item); // 執行後滿足原需求
這個例子的問題出在,第二行的 list 不是從 input得來,而是外部的變數。這樣就是 Depend on outside。因此調整過後的 function 應該把 list 放到 input裡:
// 反例(2)const addItem = (list, item) => {
list.push(item);
return list;
}const newlist = addItem(list, item); // 執行後滿足原需求
上面的例子 return的結果完全 depend 於 input,讓 output的newlist 滿足需求,但同時也改變了原本變數list 所存的內容。我們稱這種 Side Effect,為了滿足 Pure Function的條件,addItem在執行後,list還是要跟本原的值一模一樣:
// Pure Functionconst addItem = (list, item) => {
const newlist = list.concat([item]);
return newlist;
}const newlist = addItem(list, item); // 執行後滿足原需求
只要把 push 改成 concat 後,就可以得到新的結果,而且不會影響原本的 list。這就滿足 pure function 的定義。
那麼到底有哪些行為算是 Side Effect
- 時間性質的函式,setTimeout等等
- I/O:function 中執行系統存取
- DB 資料庫存取相關
- AJAX…
- console.log …
其實還是有很多,但有些 Side Effect 其實並不嚴重,例如 console.log
那麼最後要說,為什麼要用Pure Function,它的好處是:
- 可預測
- 容易組合、分割、重用
- 容易的測試、除錯
- 可快取(cache)
關於快取,可以用下面的實例說明:
const sum = (x, y) => x + y; // Pure Function
const memoSum = memo(sum); // 把Pure Function打包成有快取的方法memoSum(1, 2); // 3, 第一次呼叫,實際計算
memoSum(1, 2); // 3, 第二次以後,從快取拿結果
由於 sum是 Pure Function,所以每次計算完的結果,其實可以先存起來。之後有相同input的時侯,就不用重新計算,直接從快取吐結果就好。
memo的寫法如下 (momod是higher order funtion,但不是 Pure Function)。
const memo = (func) => {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if(!cache[key]) {
cache[key] = func.apply(func, args);
}
return cache[key];
}
}