JS 分步實現柯里化函數

簡介

首先,柯里化(Currying)是什麼呢?git

簡單說,假若有一個函數,接受多個參數,那麼通常來講就是一次性傳入全部參數並執行。
而對其執行柯里化後,就變成了能夠分屢次接收參數github

實現

階段1

如今有一個加法函數:數組

function add(x, y, z) {
  return x + y + z
}
複製代碼

調用方式是 add(1, 2, 3)閉包

若是執行柯里化,變成了 curriedAdd(),從效果來講,大體就是變成 curriedAdd(1)(2)(3) 這樣子。函數

如今先不看怎麼對原函數執行柯里化,而是根據這個調用方式從新寫一個函數。
代碼多是這樣的:post

function curriedAdd1(x) {
  return function (y) {
    return function (z) {
      return x + y + z
    }
  }
}
複製代碼

階段2

假如如今想要升級一下,不止能夠接受三個參數。
可使用 arguments,或者使用展開運算符來處理傳入的參數。ui

可是有一個衍生的問題。由於以前每次只能傳遞一個,總共只能傳遞三個,才保證了調用三次以後參數個數恰好足夠,函數才能執行。spa

既然咱們打算修改成能夠接受任意個數的參數,那麼就要規定一個終點。好比說,能夠規定爲當再也不傳入參數的時候,就執行函數。prototype

下面是使用 arguments 的實現。code

function getCurriedAdd() {
  // 在外部維護一個數組保存傳遞的變量
  let args_arr = []
  // 返回一個閉包
  let closure = function () {
    // 本次調用傳入的參數
    let args = Array.prototype.slice.call(arguments)
    // 若是傳進了新的參數
    if (args.length > 0) {
      // 保存參數
      args_arr = args_arr.concat(args)
      // 再次返回閉包,等待下次調用
      // 也能夠 return arguments.callee
      return closure
    }
    // 沒有傳遞參數,執行累加
    return args_arr.reduce((total, current) => total + current)
  }
  return closure
}
curriedAdd = getCurriedAdd()
curriedAdd(1)(2)(3)(4)()
複製代碼

階段3

這時能夠發現,上面的整個函數裏,與函數具體功能(在這裏就是執行加法)有關的,就只是當沒有傳遞參數時的部分,其餘部分都是在實現怎樣屢次接收參數。

那麼,只要讓 getCurriedAdd 接受一個函數做爲參數,把沒有傳遞參數時的那一行代碼替換一下,就能夠實現一個通用的柯里化函數了。

把上面的修改一下,實現一個通用柯里化函數,並把一個階乘函數柯里化:

function currying(fn) {
  let args_arr = []
  let closure =  function (...args) {
    if (args.length > 0) {
      args_arr = args_arr.concat(args)
      return closure
    }
    // 沒有新的參數,執行函數
    return fn(...args_arr)
  }
  return closure
}
function multiply(...args) {
  return args.reduce((total, current) => total * current)
}
curriedMultiply = currying(multiply)
console.log(curriedMultiply(2)(3, 4)())
複製代碼

階段4

上面的代碼裏,對於函數執行時機的判斷,是根據是否有參數傳入。
可是更多時候,更合理的依據是原函數能夠接受的參數的總數。

函數名的 length 屬性就是該函數接受的參數個數。好比:

function test1(a, b) {}
function test2(...args){}
console.log(test1.length) // 2
console.log(test2.length) // 0
複製代碼

改寫一下:

function currying(fn) {
  let args_arr = [], max_length = fn.length
  let closure = function (...args) {
    // 先把參數加進去
    args_arr = args_arr.concat(args)
    // 若是參數沒滿,返回閉包等待下一次調用
    if (args_arr.length < max_length) return closure
    // 傳遞完成,執行
    return fn(...args_arr)
  }
  return closure
}
function add(x, y, z) {
  return x + y + z
}
curriedAdd = currying(add)
console.log(curriedAdd(1, 2)(3))
複製代碼

Lodash 中的柯里化

Lodash 中的柯里化就靈活得多了,能夠先放置佔位符以後再傳值。
能夠參考一下《實現 lodash 的 curry 方法》,這裏就不分析了。

參考連接

大佬,JavaScript 柯里化,瞭解一下?
JS 函數柯里化
實現 lodash 的 curry 方法

相關文章
相關標籤/搜索