JavaScript中的compose函式和pipe函式

蔣鵬飛發表於2020-02-09

compose函式

compose函式可以將需要巢狀執行的函式平鋪,巢狀執行就是一個函式的返回值將作為另一個函式的引數。我們考慮一個簡單的需求:

給定一個輸入值x,先給這個值加10,然後結果乘以10
複製程式碼

這個需求很簡單,直接一個計算函式就行:

const calculate = x => (x + 10) * 10;
let res = calculate(10);
console.log(res);    // 200
複製程式碼

但是根據我們之前講的函數語言程式設計,我們可以將複雜的幾個步驟拆成幾個簡單的可複用的簡單步驟,於是我們拆出了一個加法函式和一個乘法函式:

const add = x => x + 10;
const multiply = x => x * 10;

// 我們的計算改為兩個函式的巢狀計算,add函式的返回值作為multiply函式的引數
let res = multiply(add(10));
console.log(res);    // 結果還是200
複製程式碼

上面的計算方法就是函式的巢狀執行,而我們compose的作用就是將巢狀執行的方法作為引數平鋪,巢狀執行的時候,裡面的方法也就是右邊的方法最開始執行,然後往左邊返回,我們的compose方法也是從右邊的引數開始執行,所以我們的目標就很明確了,我們需要一個像這樣的compose方法:

// 引數從右往左執行,所以multiply在前,add在後
let res = compose(multiply, add)(10);
複製程式碼

在講這個之前我們先來看一個需要用到的函式Array.prototype.reduce

Array.prototype.reduce

陣列的reduce方法可以實現一個累加效果,它接收兩個引數,第一個是一個累加器方法,第二個是初始化值。累加器接收四個引數,第一個是上次的計算值,第二個是陣列的當前值,主要用的就是這兩個引數,後面兩個引數不常用,他們是當前index和當前迭代的陣列:

const arr = [[1, 2], [3, 4], [5, 6]];
// prevRes的初始值是傳入的[],以後會是每次迭代計算後的值
const flatArr = arr.reduce((prevRes, item) => prevRes.concat(item), []);

console.log(flatArr); // [1, 2, 3, 4, 5, 6]
複製程式碼

Array.prototype.reduceRight

Array.prototype.reduce會從左往右進行迭代,如果需要從右往左迭代,用Array.prototype.reduceRight就好了

const arr = [[1, 2], [3, 4], [5, 6]];
// prevRes的初始值是傳入的[],以後會是每次迭代計算後的值
const flatArr = arr.reduceRight((prevRes, item) => prevRes.concat(item), []);

console.log(flatArr); // [5, 6, 3, 4, 1, 2]
複製程式碼

那這個compose方法要怎麼實現呢,這裡需要藉助Array.prototype.reduceRight:

const compose = function(){
  // 將接收的引數存到一個陣列, args == [multiply, add]
  const args = [].slice.apply(arguments);
  return function(x) {
    return args.reduceRight((res, cb) => cb(res), x);
  }
}

// 我們來驗證下這個方法
let calculate = compose(multiply, add);
let res = calculate(10);
console.log(res);    // 結果還是200
複製程式碼

上面的compose函式使用ES6的話會更加簡潔:

const compose = (...args) => x => args.reduceRight((res, cb) => cb(res), x);
複製程式碼

Redux的中介軟體就是用compose實現的,webpack中loader的載入順序也是從右往左,這是因為他也是compose實現的。

pipe函式

pipe函式跟compose函式的左右是一樣的,也是將引數平鋪,只不過他的順序是從左往右。我們來實現下,只需要將reduceRight改成reduce就行了:

const pipe = function(){
  const args = [].slice.apply(arguments);
  return function(x) {
    return args.reduce((res, cb) => cb(res), x);
  }
}

// 引數順序改為從左往右
let calculate = pipe(add, multiply);
let res = calculate(10);
console.log(res);    // 結果還是200
複製程式碼

ES6寫法:

const pipe = (...args) => x => args.reduce((res, cb) => cb(res), x)複製程式碼

原創不易,每篇文章都耗費了作者大量的時間和心血,如果本文對你有幫助,請點贊支援作者,也讓更多人看到本文~~

更多文章請看我的掘金文章彙總



相關文章