3

简单来说,柯里函数就是只接受一个参数的函数,柯里化的起源请参看这篇文章:函数式编程入门教程
通常来讲,如果三个数求和的函数我们会这样写:

function _sum3(x, y, z) {
    return x + y + z
}

如果只考虑实现这个函数的柯里化,我们可以这样做:

function sum3(x) {
    return function(y) {
        return function(z) {
            return x + y + z
        }
    }
}
console.log(sum3(1)(2)(3)) // 6

观察上面两种不同的写法可以发现,第二种写法其实就是首先把三个参数收集起来,然后到最后再调用第一种写法的函数:

function sum3(x) {
    return function(y) {
        return function(z) {
            return _sum3(x, y, z)
        }
    }
}
console.log(sum3(1)(2)(3)) // 6

所以柯里化的写法只是把常用写法包装了一下,可以使用一个专用的柯里化函数实现这种包装。柯里化函数是一种高阶函数,我们把它命名为curry

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

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


阿潇
123 声望15 粉丝

the frontend is a fullstack