JavaScript函数柯里化的简单实现

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

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

the frontend is a fullstack

100 声望
15 粉丝
0 条评论
推荐阅读
【隐藏坑】小程序类目变更
小程序类目变更时,如果类目有减少,可能会导致订阅消息中,模板被自动删除。微信后台会显示删除操作者是“类目变更删除”最终会导致相应模板的订阅消息功能报错失效。全程在删除类目时没有任何提醒。因此,在删除...

阿潇阅读 209

从零搭建 Node.js 企业级 Web 服务器(零):静态服务
过去 5 年,我前后在菜鸟网络和蚂蚁金服做开发工作,一方面支撑业务团队开发各类业务系统,另一方面在自己的技术团队做基础技术建设。期间借着 Node.js 的锋芒做了不少 Web 系统,有的至今生气蓬勃、有的早已夭折...

乌柏木145阅读 12k评论 10

JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...

jenemy45阅读 5.8k评论 12

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木63阅读 6k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs39阅读 6.2k评论 12

封面图
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木41阅读 7.2k评论 6

【关于Javascript】--- 正则表达式篇
基础知识一、元字符 {代码...} 二、量词 {代码...} 三、集合 字符类 {代码...} 四、分支 {代码...} 五、边界 开始结束 {代码...} 六、修饰符 {代码...} 七、贪婪模式和非贪婪模式js默认贪婪模式即最大可能的匹配...

Jerry35阅读 2.9k

the frontend is a fullstack

100 声望
15 粉丝
宣传栏