柯里化的理解和實現

閱讀學習

JavaScript專題之函數柯里化
JS 函數式編程指南html

對柯里化的理解

不少人對於柯里化的理解僅僅停留在「複用參數」上。但我認爲函數式編程思想更重要做用的是:解除函數對執行時參數的依賴,加強函數的泛化能力,讓函數僅僅包含「純粹的操做邏輯」,這些操做邏輯要用在什麼樣的輸入上,使用函數時再決定。git

使用柯里化的場景:github

  1. 想要實現某個操做邏輯。(舉一個最簡單的例子,從對象中取出某個屬性的值)
  2. 確認這個操做的輸入。(在這個例子中,輸入是對象和屬性key)
  3. 將操做的輸入做爲函數的參數,解除函數實現對【參數的具體值】的依賴。(function getProp(obj, key)
  4. 在函數體中,對這些參數執行一系列操做,實現邏輯。(function getProp(obj, key) { return obj[key]; }
  5. 獲得的函數就僅僅封裝了「操做邏輯」,函數對於操做的輸入不作任何假設,所以函數的泛化能力很強,能夠處理任何合法的輸入。(在這個例子中,getProp能夠從任何對象中獲取任何屬性)編程

    「不作任何假設」的說法其實不太準確,好比說 getProp就假設了 obj參數必須是對象,但這種假設是「完成操做邏輯」的必要要求,「不作任何多餘的假設」更準確一些。我在這裏使用更絕對的語氣,是爲了加強本身對這個觀點的印象。
  6. 當用戶使用這個函數封裝的操做邏輯時,調用這個函數,而且須要在參數中提供操做的輸入。函數執行完之後,返回操做的輸出。能夠將函數看做一個黑盒子,給什麼輸入就會返回對應的輸出,函數自己是「無狀態」的。(在這個例子中,getProp(obj1, 'key1')getProp(obj2, 'key2'),函數能適應任何合法的輸入,無論調用多少次,無論傳入什麼參數,函數的操做邏輯都不會改變)
  7. 經過柯里化,能夠在真正執行函數以前先肯定某些參數。換句話說,柯里化將輸入的函數和參數進行「綁定」,返回綁定後的函數,返回的函數期待剩餘的參數。(好比說,咱們常常要從Array的原型中獲取方法,let getPropFromArrProto = curry(getProp, Array.prototype);經過這個新的函數就能直接從Array.prototype中獲取屬性)

實現

function curry(fn, ...priorArgs) {
  const length = fn.length;
  return function judge(...restArgs) {
    return priorArgs.length + restArgs.length >= length
      ? fn.call(this, ...priorArgs, ...restArgs)
      : function (...args) { return judge.call(this, ...restArgs, ...args); }
  }
}

// 測試代碼
var fn = curry(function (a, b, c, d) {
  console.log([a, b, c, d]);
  return [a, b, c, d];
}, 'p');

fn("a", "b", "c") // [ 'p', 'a', 'b', 'c' ]
fn("a", "b")("c") // [ 'p', 'a', 'b', 'c' ]
fn("a")("b")("c") // [ 'p', 'a', 'b', 'c' ]
fn("a")("b", "c") // [ 'p', 'a', 'b', 'c' ]

其中,ide

priorArgs.length + restArgs.length >= length
      ? fn.call(this, ...priorArgs, ...restArgs)
      : function (...args) { return judge.call(this, ...restArgs, ...args); }

它的意思是,若是已經接受的參數數量很多於fn(被柯里化的函數)期待的參數數量,就調用fn並返回結果。
不然,返回一個新的函數,這個函數期待剩餘的參數。
調用這個新函數會再次進行參數數量的判斷,若是已經接受的參數數量很多於fn(被柯里化的函數)期待的參數數量,就調用fn並返回結果,不然返回一個新的函數……以此類推。函數式編程

相關文章
相關標籤/搜索