js 函数柯里化
什么是函数柯里化
维基百科上说道:柯里化,英语:Currying
(果然是满满的英译中的既视感),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
通过一个简单的例子解释下,比如有一个函数:
function addFn (x, y, z) {
return x + y + z
}
正常函数的调用是:
addFn(1, 2, 3)
而柯里化后的函数调用应该像这样:
addFn(1)(2)(3)
// 或者
addFn(1, 2)(3)
// 又或者
addFn(1)(2, 3)
这样怎么实现呢,通过简单的2个参数的加法函数演示一下,当然这是最简单的,调用的方式也只能传单个参数;
function curryAdd (x) {
return function (y) {
return x + y
}
}
curryAdd(1)(2) // 3
那么我们怎么怎么把一个普通的函数转换成形如上述curryAdd
函数的样子呢,这就需要我们进行函数的柯里化,将一个多参数的函数转变称为能够像链式调用那样的函数;
抽象上面的curryAdd
高阶函数,先假设只调用两层(fn(1)(2)
),也可以理解为被柯里化的函数只有两个参数,我们最多只能返回两层,args1
是currying
函数本身的参数,args2
是第一层的参数1
,args3
是第二层的参数2
function addFn (x, y) {
return x + y
}
function currying (fn, ...args1) {
console.log(args1, 'args1') // 由于没有传参这里是[]
return function (...args2) {
console.log(args2, 'args2') // 这里是[1]
return function(...args3) {
console.log(args3, 'args3') // 这里是 [2]
// 然后把接收到的所有参数,交由fn函数处理,然后返回
return fn(...args1, ...args2, ...args3)
}
}
}
let fn = curry(addFn)
console.log(fn(1)(2)) // 3
简单实现
问题来了,我们不可能有多少个参数就写多少层吧,我们需要定义一个柯里化函数,来进行函数的柯里化,具备通用性,无论多少参数都能进行转化;
我们可以利用函数上的特殊属性length
来进行处理,这个是函数参数的个数,比如我们定义一个函数foo(a, b, c)
,foo.length
就是函数入参的个数3
由于我们不知道具体有多少个参数,因此需要递归来进行处理,如果传入的参数没有超过函数入参的个数,就再次调用自己,进行下一轮的柯里化,直到传入的参数等于函数入参的个数
代码如下:
function simpleCurrying(fn, ...args) {
// 如果传入的参数已经大于函数可以处理的长度,则直接抛出错误
if (args.length > fn.length) {
throw new Error('参数超出可处理范围')
}
// 一旦传入的参数等于了函数可以处理的长度,则调用fn进行处理
if (args.length === fn.length) {
return fn(...args)
}
// 否则继续调用自己,注意需要在外层接收下一次调用的入参,并合并到上一次的参数中,否则参数会丢失
return function(...args2) {
return simpleCurrying(fn, ...args, ...args2)
}
}
让我们来测试一下:
function add (a, b, c, d) {
return a + b + c + d
}
let sc = simpleCurry(add);
sc(1, 2, 3, 4) // 10
sc(1, 2)(3)(4) // 10
sc(1, 2, 3)(4) // 10
sc(1, 2)(3, 4) // 10
sc(1, 2, 3, 4, 5) // ERROR 参数超出可处理范围
当然我们的例子还有许多需要优化的地方,而lodash.curry也提供了柯里化函数的方法,可以直接使用;
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com