先看代碼: let fn1 = function (x) { return x + 10; }; let fn2 = function (x) { return x * 10; }; let fn3 = function (x) { return x / 10; }; console.log(fn3 ...
先看代碼:
let fn1 = function (x) { return x + 10; }; let fn2 = function (x) { return x * 10; }; let fn3 = function (x) { return x / 10; };console.log(fn3(fn1(fn2(fn1(6)))));
這是幾個簡單的運算方法,但想輸出的是一個多層函數嵌套的運行結果,即把前一個函數的運行結果賦值給後一個函數,當然我們可以寫成一下這樣:
let x = fn1(6); x = fn2(x); x = fn1(x); x = fn3(x);
但現在我就想用一個函數解決這種問題,形如:
compose(fn1, fn2, fn1, fn3)(6);
這個compose函數就是這篇文章介紹的——函數調用的扁平化,即把層級嵌套的那種函數調用(一個函數的運行結果當作實參傳給下一個函數的這種操作)扁平化,這就是compose函數。
那麼下麵就是開始實現這個函數:
首先我們看參數,需要給出不確定個數的函數:
function compose(...funcs) { //=>funcs:傳遞的函數集合 }
compose函數執行後跟個(),說明函數執行完再執行一個函數,即函數執行完會返回一個新函數,而且也會給出第一次調用函數時的參數:
function compose(...funcs) { //=>funcs:傳遞的函數集合 return function proxy(...args) { //=>args:第一次調用函數傳遞的參數集合 } }
繼續往下進行,我們需要判斷給出的函數集合的個數,如果沒有給函數,我們只需將後一個的參數返回,如果只給出一個函數,我們只需把後一個的參數賦給這個函數去執行即可:
function compose(...funcs) { //=>funcs:傳遞的函數集合 return function proxy(...args) { //=>args:第一次調用函數傳遞的參數集合 let len = funcs.length; if (len === 0) { //=>一個函數都不需要執行,直接返回ARGS return args; } if (len === 1) { //=>只需要執行一個函數,把函數執行,把其結果返回即可 return funcs[0](...args); } }; }
如果給出的參數集合是兩個及以上,那就是把前一個函數的執行結果賦給後一個函數,說到這,應該會想到一個滿足這個需求的數組方法——reduce:
function compose(...funcs) { //=>funcs:傳遞的函數集合 return function proxy(...args) { //=>args:第一次調用函數傳遞的參數集合 let len = funcs.length; if (len === 0) { //=>一個函數都不需要執行,直接返回ARGS return args; } if (len === 1) { //=>只需要執行一個函數,把函數執行,把其結果返回即可 return funcs[0](...args); } return funcs.reduce((x, y) => { }); }; }
但這裡需要註意的是,第一次執行的時候,參數x是個函數,之後再執行的時候x是個函數執行的結果,所以需要進行判斷:
function compose(...funcs) { //=>funcs:傳遞的函數集合 return function proxy(...args) { //=>args:第一次調用函數傳遞的參數集合 let len = funcs.length; if (len === 0) { //=>一個函數都不需要執行,直接返回ARGS return args; } if (len === 1) { //=>只需要執行一個函數,把函數執行,把其結果返回即可 return funcs[0](...args); } return funcs.reduce((x, y) => { return typeof x === "function" ? y(x(...args)) : y(x) }); }; }
這樣,compose函數完成。
當然,redux源碼中的compose.js也可以實現一開始想要的效果:
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函數有些不同,它執行的順序是函數集合中的函數從後往前執行,所以結果也會不同:
compose(fn1, fn2, fn1, fn3)(6)); //=> 用第一個compose執行的結果是17,用redux的執行結果是116