JavaScript 柯里化把“大餐”拆成“小炒”的艺术在函数式编程中柯里化Currying是一个高频词汇。很多初学者看到类似add(1)(2)(3)这样的代码时会一头雾水为什么函数可以这样调用它到底有什么用别急今天我们就把这个看似复杂的概念嚼碎了喂给你。 目录 什么是柯里化 生活化比喻从“满汉全席”到“自助套餐” 代码对比普通函数 vs 柯里化函数️ 手写一个通用的柯里化工具函数 柯里化的三大实战场景 总结1. 什么是柯里化官方定义柯里化Currying是把接受多个参数的函数变换成接受一个单一参数最初函数的第一个参数的函数并且返回接受余下的参数且返回结果的新函数的技术。通俗解释原本你需要一次性给函数所有参数它才干活。柯里化之后你可以分批给参数。每给一个参数它就返回一个新函数等着接收下一个参数直到所有参数都给齐了它才最终执行并返回结果。核心特征参数分批传递fn(a, b, c)变成fn(a)(b)(c)。延迟执行参数没给够之前不执行具体逻辑只返回新函数。参数复用固定的参数可以先传进去生成一个“半成品”函数供后续使用。2. 生活化比喻从“满汉全席”到“自助套餐”想象你去餐厅吃饭❌普通函数你必须一次性点完所有菜主食、饮料、甜点厨房才开始做。如果你忘了点饮料就得重新下单或者等下次再来。cook(牛排, 可乐, 冰淇淋)✅柯里化函数你采用自助餐模式。你先拿了一个盘子放了牛排。返回一个新状态盘子里有牛排接着你又拿了可乐。返回一个新状态盘子里有牛排可乐最后你拿了冰淇淋。此时盘子满了厨房开始制作套餐端给你。cook(牛排)(可乐)(冰淇淋)好处是什么如果你每天都吃牛排你可以先预置一个“牛排盘”每天只需要决定加什么饮料和甜点即可。这就是“参数复用”3. 代码对比普通函数 vs 柯里化函数假设我们要实现一个简单的加法函数。❌ 普通函数functionadd(x,y,z){returnxyz;}// 必须一次性传入所有参数console.log(add(1,2,3));// 6✅ 柯里化函数functioncurriedAdd(x){returnfunction(y){returnfunction(z){returnxyz;};};}// 可以分批传入参数conststep1curriedAdd(1);// 返回一个函数等待接收 yconststep2step1(2);// 返回一个函数等待接收 zconstresultstep2(3);// 所有参数齐了执行计算返回 6// 或者链式调用console.log(curriedAdd(1)(2)(3));// 6看起来代码变多了是的手动写柯里化很麻烦。所以我们需要一个通用的工具函数来自动完成这个转换。4. ️ 手写一个通用的柯里化工具函数在实际开发中我们不会为每个函数都手动嵌套闭包。我们会编写一个高阶函数curry它能把任何普通函数转换成柯里化函数。 实现代码/** * 通用柯里化函数 * param {Function} fn - 需要被柯里化的原函数 * returns {Function} - 柯里化后的新函数 */functioncurry(fn){// 获取原函数期望的参数个数constarityfn.length;returnfunctionjudge(...args){// 如果当前收集的参数个数 原函数期望的参数个数if(args.lengtharity){// 执行原函数返回结果returnfn.apply(null,args);}else{// 否则返回一个新函数继续收集剩余参数returnfunction(...restArgs){// 递归调用 judge将已收集的参数和新参数合并returnjudge.apply(null,args.concat(restArgs));};}};} 测试一下// 1. 定义一个普通函数functionsum(a,b,c){returnabc;}// 2. 将其柯里化constcurriedSumcurry(sum);// 3. 各种调用方式均有效console.log(curriedSum(1,2,3));// 6 (一次性传完)console.log(curriedSum(1)(2,3));// 6 (分批传)console.log(curriedSum(1)(2)(3));// 6 (完全柯里化)// 4. 参数复用示例constadd10curriedSum(10);console.log(add10(20,30));// 60 (10 20 30)console.log(add10(1,2));// 13 (10 1 2)5. 柯里化的三大实战场景柯里化不仅仅是炫技它在实际开发中有巨大的价值。场景一参数复用固定配置这是柯里化最核心的用途。当你有一个函数其中某些参数是固定的只有少数参数变化时柯里化可以帮你创建一个“专用版本”的函数。例子正则验证// 普通写法每次都要传正则表达式functioncheck(reg,txt){returnreg.test(txt);}check(/\d/g,test);// falsecheck(/[a-z]/g,test);// true// 柯里化写法预先固定正则生成专用验证器constcurriedCheckcurry(check);consthasNumbercurriedCheck(/\d/g);consthasLettercurriedCheck(/[a-z]/g);// 后续使用极其简洁且语义清晰hasNumber(test123);// truehasLetter(123);// false场景二延迟执行在某些场景下我们希望函数先接收部分参数但不立即执行直到满足特定条件或收集完所有参数后再执行。例子日志打印functionlog(level,date,msg){console.log([${level}]${date}:${msg});}constcurriedLogcurry(log);// 预设级别和时间constinfoLogcurriedLog(INFO)(newDate().toLocaleDateString());// 在代码的不同地方只关心消息内容infoLog(用户登录成功);infoLog(数据加载完成);场景三兼容性处理与函数组合在函数式编程库如 Lodash、Ramda中柯里化是函数组合Compose的基础。只有当函数都是单参数或柯里化后表现为单参数流时才能像管道一样轻松串联。// 假设我们有 lodash 的 curry 和 flowimport{curry,flow}fromlodash;constaddcurry((a,b)ab);constmultiplycurry((a,b)a*b);// 组合函数先加 10再乘 2constadd10ThenMultiply2flow(add(10),multiply(2));console.log(add10ThenMultiply2(5));// (5 10) * 2 30 总结特性普通函数柯里化函数参数传递一次性传递所有参数分批传递一次一个或一组执行时机调用即执行参数凑齐后才执行延迟执行主要优势简单直观参数复用、延迟执行、易于组合适用场景大多数常规业务逻辑配置项固定、函数式编程、高阶组件封装 博主寄语柯里化本质上是一种**“降维打击”**的思维它将一个多参问题分解为多个单参问题的序列。不要为了柯里化而柯里化。当发现你在重复传递相同的参数时就是使用柯里化的最佳时机。记住口诀多参变单参闭包来帮忙。参数若不够返回新函数。参数若凑齐执行出结果。复用与延迟代码更优雅。希望这篇文档能帮你彻底掌握柯里化如果有疑问欢迎在评论区留言。喜欢这篇文章吗记得点赞、收藏、转发哦❤️