为什么我要说:柯里化 == 闭包+递归?

简介: 柯里化是 JS 高程中不可或缺的重心,本篇带你来冲一冲它!!

柯里化是 JS 高程中不可或缺的重心,本篇带你来冲一冲它!!

image.png


我们不妨以两数相加为例子,递进说明。

我们通常是这样写一个函数来求得 两数相加 的值:


function sum(a,b){
    console.log(a+b)
}
sum(1,2)


这样写一点毛病没有!


不过呢?问题总会在发展中产生,产品经理又要加一个值,需求:三数相加;

咱通常来说,第一时间,就在原基础上,直接再加一个参数就是了;


于是,修改后像是这样:


function sum(a,b,c){
    console.log(a+b+c)
}
sum(1,2,3)


问:这样写,有毛病吗??

答:太有毛病了!


这样一改,既违反了:“开闭原则”、又违反了:“单一职责原则”。


为不太熟悉设计原则的小伙伴们,简单解释下:

  • 什么是“开闭原则”?即:我们编程中要尽可能的避免直接修改函数、类或模块,而是要在原有基础上拓展它;
  • 什么是“单一职责原则”?即:每个函数、类或模块,应该只负责一个单一的功能;


首先,咱修改了 sum 函数的传参以及内部的调用 ⇒ 则违反“开闭原则”

其次,sum 函数本来只负责两数相加,修改后,它又负责三数相加,职责已经发生了变化 ⇒ 则违反 “单一职责原则”;


如果正规按照单一责任来写,应该是:

// 负责两数相加
function sum2(a,b){
    console.log(a+b)
}
// 负责三数相加
function sum3(a,b,c){
    console.log(a+b+c)
}


事实上,是不可能这样去写的,因为如果有一万个数相加,得写一万个函数。

而 加法只有一个!! 不管你最终要加几个值,总是要一个加一个。

于是乎,我们设想,能不能写一个这样的函数:它的功能,就是“加”,参数跟几个,我就加几个。


// 负责“加法”,
function addCurry(){
    ...
    ...
    ...
}
addCurry(1)(2) // 两数相加
addCurry(1)(2)(3) // 三数相加
...
addCurry(1)(2)(3)...(n) // n 数相加


没错,这个函数就是:柯里化!!(或者说这个过程叫柯里化,这个思想叫柯里化,本瓜认为这里不需要太死扣定义)


接着,我们一步步来试试,它会是怎样构成的?

为了能够实现一个加一个,即存储参数的目的,我们想一想,还有什么法宝?

没错,JS 奥义:闭包!


其实,本瓜时常想,闭包的终极秘密是什么?最后将其理解为 4 个金光闪闪的大字:延迟处理!


什么意思?简单解释下:

function directHandle(a,b){
    console.log("直接处理",a,b)
}
directHandle(111,222)
// 直接处理 111 222
function delayHandle(a){
    return function(b){
         console.log("延迟处理",a,b)
    }
}
delayHandle(111)
// ƒ (b){
//      console.log("延迟处理",a,b)
//  }


如上 delayHandle(111) 不像 directHandle(111,222) 直接打印值,而是先返回一个函数 f(b);111 也被临时保存了,delayHandle(111)(222),则得到相同的输出。这就是:延迟处理的思想。


另外补一句:延迟处理是函数式编程的精华所在,在不能保证每个函数都是纯函数的前提下,在管道处理的最后,再进行处理,能最大程度的保证减少副作用。也就是 Monad 思想,此处不做展开。


言归正传,于是乎,我们借用闭包来实现最初版的柯里化:

// 两数相加
function addCurry(a){
    return function(b){
            console.log(a+b)
    }
}
addCurry(1)(2)
// 三数相加
function addCurry(a){
    return function(b){
        return function(c){
             console.log(a+b+c)
        }
    }
}
addCurry(1)(2)(3)


写两个闭包的过程,聪明的你一定就明白了,这样一直写下去,不就是递归吗?!

于是乎,我们知道,当参数是 n 个的时候,需要递归 n-1 次 return function

于是乎,addCurry 写法如下:


let arr = []
 function addCurry() {
     let arg = Array.prototype.slice.call(arguments); // 递归获取后续参数
     arr = arr.concat(arg);
      if (arg.length === 0) { // 如果参数为空,则判断递归结束
          return arr.reduce((a,b)=>{return a+b}) // 求和
      } else {
          return addCurry;
      }
  }
addCurry(1)(2)(3)()


OK,至此,,大功告成!!

以上,用最简单的代码解释了 —— 为什么我说:柯里化 == 闭包+递归 ?

柯里化是一种思想,上面的 addCurry 可以说是最简单的一种实践。在函数式编程中,Curry 更是大放异彩,比如 compose(fn1)(fn2)(fn3)…(fnN)(args) 等等。

如果以后有人再问你柯里化,可以往这个方向上答。。。


相关文章
|
监控 数据可视化 项目管理
CM模式是什么?如何应用?
CM 模式(Construction Management)即建设管理模式,起源于20世纪60年代的美国,通过专业的建设管理团队在项目早期介入,优化设计方案,协调各方资源,有效提升项目质量和进度控制。该模式已广泛应用于各类建筑工程,并不断创新发展,适应数字化、绿色建筑及国际化需求。未来,CM模式将继续推动建筑行业的进步。
2316 2
|
存储 JSON 监控
eBPF 深度探索: 高效 DNS 监控实现(下)
eBPF 深度探索: 高效 DNS 监控实现(下)
1381 0
|
8月前
|
人工智能 C++
AI音乐100%有版权的路劲是什么?AI音乐的版权处理卓伊凡
AI音乐100%有版权的路劲是什么?AI音乐的版权处理卓伊凡
602 11
|
3月前
|
传感器 存储 算法
40人 vs 150人:我们用真实课堂数据,画出了“闷热曲线”
本文通过在5种真实教室场景中开展2小时密闭监测实验,验证温湿度预警系统实效:VENTIL通风建议比MUGGY!闷热报警平均提前20–40分钟,证实趋势预警的有效性;同时发现人员密度比绝对人数更关键,大空间需引入体积系数优化阈值。数据驱动,直击工程落地痛点。
250 3
|
7月前
|
人工智能 算法 安全
AI Agent 职业路线:从工具使用者到智能体生态构建者
本文探讨AI Agent驱动的职场范式转移:从人机交互迈向人机协同。提出技术从业者三层角色(执行者、协作者、生态管理者),并系统阐述上下文工程、输出校验、智能体调优三大核心能力,以及单点应用→多体编排→生态治理的进阶路径。(239字)
456 5
|
8月前
|
人工智能 小程序 JavaScript
uni-app微信小程序相机组件二次拍照白屏问题的排查与解决
本文分享了在uni-app开发微信小程序时,因状态管理不当导致拍照后图片不显示的bug排查过程。问题根源在于删除照片时将变量设为“#”(truthy值),导致条件渲染逻辑错误。通过彻底清空相关状态并遵循最佳实践,最终解决问题。
337 2
|
UED
一些 CSS3 动画的实际应用案例
一些 CSS3 动画的实际应用案例
658 57
|
弹性计算 缓存 前端开发
阿里云服务器配置选择指南2核4G、4核8G和8核16G性能全解析!
阿里云2核4G、4核8G与8核16G服务器配置解析及选择指南。根据不同业务需求,推荐ECS经济型e、通用算力型u1、计算型c7/c8i实例。企业用户专享2核4G ECS u1实例仅199元/年,自带5M带宽;4核8G u1实例起价955.58元/年;8核16G c7实例起价6544元/年。各实例CPU性能各异,如c7采用第三代Intel Xeon处理器,适配高性能计算场景。
1388 10
|
存储 Linux 文件存储
在Linux中,raid0、raid1、raid5 三种工作模式的工作原理及特点?
在Linux中,raid0、raid1、raid5 三种工作模式的工作原理及特点?
|
存储 Linux 数据库
运维系列.在Docker中使用Grafana(二)
运维系列.在Docker中使用Grafana(二)
1057 4

热门文章

最新文章