高階函數
滿足以下兩點的函數:
- 函數可以作爲參數被傳遞
- 函數可以作爲返回值輸出
叫高階函數,很顯然js中的函數滿足高階函數的條件。
函數作爲參數:
function pow(x) {
return x * x;
}
const arr = [1, 2, 3];
const res = arr.map(pow);
函數作爲返回值:
function getPrintFn() {
function print(msg) {
console.log(msg);
}
return print;
}
高階函數與函數式編程有什麼關係?通過上一篇我們知道函數式編程採用純函數,那怎麼把不純的函數轉化爲一個純函數呢?通過把不純的操作包裝到一個函數中,再返回這個函數(即上面的例子),即可達到目的。
柯里化(curry)
只傳遞給函數一部分參數來調用它,讓它返回一個函數去處理剩下的參數。
特點:
接收單一參數,將更多的參數通過回調函數來搞定;
返回一個新函數,用於處理所有的想要傳入的參數;
需要利用call/apply與arguments對象收集參數;
返回的這個函數正是用來處理收集起來的參數;
function add(x, y) {
return x + y;
}
// 柯里化
function add(x) {
return function(y) {
return x + y;
}
}
const increment = add(1);
increment(2); // 3
當我們談論純函數的時候,我們說它們接受一個輸入返回一個輸出。curry 函數所做的正是這樣:每傳遞一個參數調用函數,就返回一個新函數處理剩餘的參數。這就是一個輸入對應一個輸出。curry函數適用於以下場景:
- 延遲執行:不斷的柯里化,累積傳入的參數,最後執行。
- 固定易變因素:提前把易變因素,傳參固定下來,生成一個更明確的應用函數。最典型的代表應用,是bind函數用以固定this這個易變對象。
代碼組合(compose)
在函數式編程中,通過將一個個功能單一的純函數組合起來實現一個複雜的功能,就像樂高拼積木一樣,這種稱爲函數組合(代碼組合)。下面看一個例子:
最佳實踐是讓組合可重用。
函子
我們知道,函數式編程實質是通過管道把數據在一系列純函數間傳遞,但是,控制流(control flow)、異常處理(error handling)、異步操作(asynchronous actions)和狀態(state)呢?還有更棘手的副作用(effects)呢?這些問題的解決就要引入函子的概念了。
我們首先定義一個容器,用來封裝數據
函子封裝了數據和對數據的操作,functor 是實現了map函數並遵守一些特定規則的容器類型。
把值裝進一個容器,而且只能使用 map 來處理它,這麼做的理由到底是什麼呢?
讓容器自己去運用函數能給我們帶來什麼好處?
Functor 是一個對於函數調用的抽象,我們賦予容器自己去調用函數的能力。當 map 一個函數時,我們讓容器自己來運行這個函數,這樣容器就可以自由地選擇何時何地如何操作這個函數,以致於擁有惰性求值、錯誤處理、異步調用等等非常牛掰的特性。
函子的類型
1.Maybe(處理null問題)
2.Either(if…else)
3.IO(IO、網絡請求、DOM)
4.Monad(嵌套問題)
Maybe
一種用來處理null和undefined問題的函子,避免繁瑣的手動判空操作
Either
一種用來處理if…else問題的函子
IO
通過返回一個獲取數據的函數來延遲IO的副作用,等調用者去執行有副作用的函數,
以保證獲取數據過程中的無副作用特性
Monad
monad 是可以變扁(flatten)的實現了of方法的 functor
總結
學習函數式編程,實際上就是學習函子的各種運算。由於可以把運算方法封裝在函子裏面,所以又衍生出各種不同類型的函子,有多少種運算,就有多少種函子。函數式編程就變成了運用不同的函子,解決實際問題。
參考文檔
https://github.com/xitu/gold-...
https://llh911001.gitbooks.io...