簡單來講,柯里函數就是隻接受一個參數的函數,柯里化的起源請參看這篇文章:函數式編程入門教程
一般來說,若是三個數求和的函數咱們會這樣寫:javascript
function _sum3(x, y, z) { return x + y + z }
若是隻考慮實現這個函數的柯里化,咱們能夠這樣作:html
function sum3(x) { return function(y) { return function(z) { return x + y + z } } } console.log(sum3(1)(2)(3)) // 6
觀察上面兩種不一樣的寫法能夠發現,第二種寫法其實就是首先把三個參數收集起來,而後到最後再調用第一種寫法的函數:java
function sum3(x) { return function(y) { return function(z) { return _sum3(x, y, z) } } } console.log(sum3(1)(2)(3)) // 6
因此柯里化的寫法只是把經常使用寫法包裝了一下,可使用一個專用的柯里化函數實現這種包裝。柯里化函數是一種高階函數,咱們把它命名爲curryes6
function curry(fn) { return function(y) { return function(z) { return fn(x, y, z) } } } var sum3 = curry((x, y, z) => { return x + y + z }) console.log(sum3(1)(2)(3)) // 6
若是有要寫一種更加通用的,能夠柯里化擁有任意多個參數的函數呢,好比sumN(1)(2)(3)...(N),按照以前的寫法,大概是這個樣子的:編程
function curryN(fn) { return function(a1) { return function(a2) { return function(a3) { //...... return function(aN) { return fn(a1, a2, a3, ...aN) } } } } }
很容易想到能夠用一個遞歸函數來簡化這種寫法,將上面那些看起來類似的函數結構命名爲nest,就能夠寫爲:數組
function nest(fn) { return function(x) { return nest(fn) } } function curry(fn) { nest(fn) }
這裏缺乏一個循環終止的判斷,因此nest函數先引入一個新參數i,當i === N時遞歸終止函數式編程
function nest(fn, i) { return function(x) { if (i === N) { return fn(...) } return nest(fn, i + 1) } } function curry(fn) { return nest(fn, 1) }
接着,須要一個存聽任意多個參數的數組,將這個數組命名爲args,而後傳入nest函數函數
function nest(fn, i, args) { return function(x) { args.push(x) if (i === fn.length) { return fn(...args) } return nest(fn, i + 1, args) } } function curry(fn) { const args = [] return nest(fn, 1, args) }
最後在添加一個處理0個參數的狀況,咱們就完成了最終版的柯里化函數測試
function curry(fn) { if (fn.length === 0) { return fn } const args = [] return nest(fn, 1, args) }
測試一下,在線demo:code
const log1 = curry((x) => console.log(x)) log1(10) // 10 const mul3 = curry((x, y, z) => console.log(x*y*z)) mul3(2)(3)(4) // 24