函數式編程(二)

高階函數

滿足以下兩點的函數:

  1. 函數可以作爲參數被傳遞
  2. 函數可以作爲返回值輸出

叫高階函數,很顯然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)

在函數式編程中,通過將一個個功能單一的純函數組合起來實現一個複雜的功能,就像樂高拼積木一樣,這種稱爲函數組合(代碼組合)。下面看一個例子:

clipboard.png

最佳實踐是讓組合可重用。

函子

我們知道,函數式編程實質是通過管道把數據在一系列純函數間傳遞,但是,控制流(control flow)、異常處理(error handling)、異步操作(asynchronous actions)和狀態(state)呢?還有更棘手的副作用(effects)呢?這些問題的解決就要引入函子的概念了。

我們首先定義一個容器,用來封裝數據

clipboard.png

函子封裝了數據和對數據的操作,functor 是實現了map函數並遵守一些特定規則的容器類型。

clipboard.png

把值裝進一個容器,而且只能使用 map 來處理它,這麼做的理由到底是什麼呢?
讓容器自己去運用函數能給我們帶來什麼好處?
Functor 是一個對於函數調用的抽象,我們賦予容器自己去調用函數的能力。當 map 一個函數時,我們讓容器自己來運行這個函數,這樣容器就可以自由地選擇何時何地如何操作這個函數,以致於擁有惰性求值、錯誤處理、異步調用等等非常牛掰的特性。

函子的類型

1.Maybe(處理null問題)
2.Either(if…else)
3.IO(IO、網絡請求、DOM)
4.Monad(嵌套問題)

Maybe

一種用來處理null和undefined問題的函子,避免繁瑣的手動判空操作

clipboard.png

Either

一種用來處理if…else問題的函子

clipboard.png

IO

通過返回一個獲取數據的函數來延遲IO的副作用,等調用者去執行有副作用的函數,
以保證獲取數據過程中的無副作用特性

clipboard.png

Monad

monad 是可以變扁(flatten)的實現了of方法的 functor

clipboard.png

總結

學習函數式編程,實際上就是學習函子的各種運算。由於可以把運算方法封裝在函子裏面,所以又衍生出各種不同類型的函子,有多少種運算,就有多少種函子。函數式編程就變成了運用不同的函子,解決實際問題。

參考文檔
https://github.com/xitu/gold-...
https://llh911001.gitbooks.io...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章