export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
複製代碼
須要說明的是,compose
函數的存在實際上是服務於中間件的,即當咱們使用applyMiddleware
實現功能加強的背後,其實就是利用了compose
函數將這多個加強函數進行合理的組合。javascript
首先,compose
函數利用了es6的rest參數,將多個類型爲函數的參數收集到一個叫做funcs
的數組中,而後進行進一步邏輯判斷,最終結果都會是返回一個函數,保證輸入輸出。java
來用例子說話吧:es6
// 初始數據
let data = 1;
// 加1函數
const add1 = v => v + 1;
// 乘2函數
const multi2 = v => v * 2;
// 1. compose沒有參數
const func1 = compose();
func1(data); // -> 1
// 2. compose有一個參數,爲add1
const func2 = compose(add1);
func2(data); // -> 2,運算順序爲 1 + 1
// 3. compose有兩個參數,順序依次爲add1與multi2
const func3 = compose(add1, multi2);
func3(data); // -> 3,運算順序爲 1 * 2 + 1
// 4. compose有兩個參數,順序依次爲multi2與add1
const func4 = compose(multi2, add1);
func4(data); // -> 4,運算順序爲 (1 + 1) * 2
複製代碼
這裏重點關注一下func3
與func4
能夠看出,接受參數相同可是順序不一樣,也會致使結果的不一樣,很明顯,越是在後面的函數,執行的次序越靠前,從源碼中的a(b(...args))
也能夠獲得印證。redux
以上其實就是redux中間件的基本實現原理,關於中間件,咱們後續再說。數組
聊完了compose
這個組合函數,順便來聊一下柯里化
這個概念吧,由於這個概念在你編寫本身的中間件的時候可能會涉及到,下面是來自於百度百科的解釋:app
在計算機科學中,柯里化(Currying)是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數且返回結果的新函數的技術。函數
若是須要舉例,那麼也很簡單,就像這樣子:post
// 如今有個add函數接受兩個參數a、b,並返回a + b
const add = (a, b) => a + b;
add(1, 2); // -> 3
// 如今用柯里化的思想來實現一下
const addByCurrying = a => b => a + b;
addByCurrying(1)(2); // -> 3
複製代碼
能夠看到其實使用柯里化就是將本來接受多個參數的函數變爲一次只接受一個參數,並返回一個新的函數用來處理剩餘的函數,就像是add
改寫爲addByCurrying
後,實現一個a+b
實際上是利用了兩個函數調用完成的,第一次接受1,第二次接受2。spa
講完了柯里化的含義,那麼爲何要費勁套這麼多層去實現一個a+b?那確定是有他的好處的,想了解具體的就移步這裏——詳解JS函數柯里化,感受這篇文章中寫的很清楚了。rest