JavaScript函數柯里化的簡單實現

簡單來講,柯里函數就是隻接受一個參數的函數,柯里化的起源請參看這篇文章:函數式編程入門教程
一般來說,若是三個數求和的函數咱們會這樣寫: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)
}

測試一下,在線democode

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

參考文章

Currying in JS
Currying in JavaScript ES6

相關文章
相關標籤/搜索