告别重复传参!用柯里化提升代码优雅度

柯里化:让函数"慢慢来",一次只吃一口

在编程世界里,我们常常会遇到这样一种场景:一个函数需要多个参数才能完成任务。但有时候,这些参数并不会一下子全部准备好------可能今天知道第一个,明天拿到第二个,后天才凑齐全部。

这时候,柯里化(Currying) 就派上用场了。它就像一位耐心的厨师,不急着把整道菜做完,而是先记住你已经给的食材,等你把剩下的材料陆续送来,再一锅炒好。


从最简单的加法说起

假设我们有一个普通的加法函数:

js 复制代码
function add(a, b) {
  return a + b;
}
console.log(add(1, 2)); // 输出 3

这很直接:两个数一起传进去,立刻出结果。但如果我只能先给你 a,过一会儿再告诉你 b 呢?普通函数就无能为力了。

于是我们可以手动"柯里化"一下:

js 复制代码
function add(a) {
  return function(b) {
    return a + b;
  };
}
console.log(add(1)(2)); // 输出 3

你看,现在 add(1) 返回的是一个新函数,这个函数"记得"了 a = 1,等你再调用它传入 b,就能算出结果。这背后靠的是 闭包------内部函数可以"记住"外部函数的变量,即使外部函数已经执行完了。

这种写法虽然可行,但只适用于固定两个参数的情况。如果函数有三个、四个甚至更多参数,手动嵌套写起来会非常繁琐,而且难以复用


自动柯里化:通用解决方案

手动为每个函数写柯里化版本太麻烦了。有没有办法写一个"万能工具",自动把任意多参函数变成可逐步传参的形式?

当然有!来看这个通用的 curry 函数:

js 复制代码
function add(a, b, c, d) {
  return a + b + c + d;
}

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn(...args); // 参数够了,直接执行
    }
    return (...rest) => curried(...args, ...rest); // 不够?继续收
  };
}

const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)(4)); // 10
console.log(curriedAdd(1, 2)(3, 4)); // 10,也可以一次传多个

它是怎么工作的?

  • fn.length 是 JavaScript 中函数的一个属性,表示该函数声明时定义的参数个数(不包括剩余参数)。
  • 每次调用 curried,都会收集当前传入的参数(通过 ...args)。
  • 如果当前参数数量 ≥ 原函数所需参数数量,就立即执行 fn(...args)
  • 否则,返回一个新函数,这个新函数会把已有的 args 和后续传入的 rest 合并,再次调用 curried ------ 这就是递归的思想。

只要收集到足够数量的参数,就立刻执行;否则,返回一个新函数继续等待。

🔍 注意 :这个实现利用了 闭包 + 递归 的思想。每次调用都把已有的参数"存起来",直到攒够为止。而闭包保证了这些中间参数不会丢失。


实战:日志函数的柯里化妙用

柯里化不只是炫技,它在实际开发中非常有用。比如处理日志:

js 复制代码
const log = type => message => {
  console.log(`${type}: ${message}`);
};

const errorLog = log('ERROR');
const infoLog = log('info');

errorLog('接口异常');        // 输出: ERROR: 接口异常
infoLog('页面加载完成');     // 输出: info: 页面加载完成

这里,log 是一个柯里化函数。我们先固定日志类型(如 'ERROR'),得到一个专门打错误日志的函数 errorLog。以后只要传消息内容就行,不用每次都写类型。

优势在哪里?

  1. 减少重复代码 :不需要每次写 log('error', 'xxx')
  2. 提高可读性errorLog('xxx')log('error', 'xxx') 更直观。
  3. 便于组合与复用:可以轻松创建不同级别的日志器,并在多个模块中共享。

总结:柯里化的三大核心

  1. 闭包:保存已传入的参数,不会被垃圾回收。
  2. 递归/链式调用:每次返回新函数,继续接收剩余参数。
  3. 退出条件 :当参数数量达到原函数要求(fn.length),就执行并返回结果。

柯里化不是必须用的技术,但它能让你的函数更灵活、更具组合性。就像乐高积木------你可以先拼好一部分,等需要时再接上其他模块,最终搭出完整作品。

下次当你发现某个函数总是在不同地方传相同的前几个参数时,不妨试试柯里化------让函数学会"等一等",说不定代码会变得更优雅!

相关推荐
threelab12 小时前
Three.js 数学函数着色器 | 三维可视化 / AI 提示词
javascript·人工智能·着色器
罗超驿12 小时前
19.告别复杂SQL!用MySQL视图把逻辑拆成“变量”式操作
数据库·mysql·面试
卷帘依旧13 小时前
React Hook采用环形链表的原因
前端
lichenyang45313 小时前
从 HarmonyOS AI 聊天模块理解工程化架构:MVVM、Controller、Provider、请求封装与 SSE
前端
卷帘依旧13 小时前
为什么React Hooks不能用在if/for等条件/循环语句中
前端
ZC跨境爬虫13 小时前
跟着 MDN 学CSS day_3:(为一个传记页面添加样式)
前端·javascript·css·ui·音视频·html5
夜雪闻竹13 小时前
sql.js WASM 实战:浏览器里跑 SQLite
javascript·sql·wasm
从文处安14 小时前
「前端何去何从」混乱到有序的状态管理: Reducer 与 Context
前端·react.js
爱喝铁观音的谷力景辉14 小时前
在Cesium中实现带箭头方向路线样式的技术详解
javascript·cesium
Qhappy14 小时前
AI逆向实战:从零还原某航空App的AES加密
javascript·后端