柯里化(Currying)
一句话理解
柯里化就是把 f(a, b, c) 变成 f(a)(b)(c)
把一个接收多个参数的函数,拆成一系列只接收一个参数的函数链。
为什么需要它?
假设你有一个日志函数:
function log(level, message) {
console.log(`[${level}] ${message}`);
}
log('INFO', '系统启动');
log('INFO', '用户登录');
log('INFO', '数据加载完成');
每次都要重复写 'INFO',很烦。柯里化可以解决:
const log = level => message => console.log(`[${level}] ${message}`);
const info = log('INFO'); // 先固定 level
const error = log('ERROR');
info('系统启动'); // [INFO] 系统启动
info('用户登录'); // [INFO] 用户登录
error('连接失败'); // [ERROR] 连接失败
效果:把公共参数提前固定,后面只用关注变化的参数。
基础实现
手动写一个简单的柯里化函数
function curry(fn) {
return function curried(...args) {
// 如果参数够了,直接执行原函数
if (args.length >= fn.length) {
return fn.apply(this, args);
}
// 否则继续收集参数
return (...nextArgs) => curried(...args, ...nextArgs);
};
}
// 使用
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
curriedAdd(1)(2)(3); // 6
curriedAdd(1, 2)(3); // 6(也支持一次传多个)
用箭头函数手动柯里化(最简单直观)
// 三步走
const add = a => b => c => a + b + c;
add(1)(2)(3); // 6
常见应用场景
1. 配置函数
// 网络请求
const request = baseURL => endpoint => params =>
fetch(`${baseURL}${endpoint}`, params);
const api = request('https://api.softool.cn');
const getUser = api('/user');
const getPosts = api('/posts');
getUser({ id: 1 }); // 请求 https://api.softool.cn/user?id=1
getPosts({ page: 1 }); // 请求 https://api.softool.cn/posts?page=1
2. 事件处理
const handleClick = action => event => {
console.log(`执行操作: ${action}`);
// 处理点击...
};
const saveClick = handleClick('保存');
const deleteClick = handleClick('删除');
button.addEventListener('click', saveClick);
deleteBtn.addEventListener('click', deleteClick);
3. 数据转换管道
const map = fn => arr => arr.map(fn);
const filter = fn => arr => arr.filter(fn);
const reduce = fn => init => arr => arr.reduce(fn, init);
const double = map(x => x * 2);
const even = filter(x => x % 2 === 0);
double([1, 2, 3]); // [2, 4, 6]
even([1, 2, 3, 4]); // [2, 4]
和普通函数的区别
| 普通函数 | 柯里化函数 |
|---|---|
add(1, 2, 3) |
add(1)(2)(3) |
| 一次性给完所有参数 | 分步给参数 |
| 不能中间暂停 | 每步都可以复用中间结果 |
| 适合简单调用 | 适合参数固定场景 |
注意事项
- 不要滥用:不是所有函数都需要柯里化,只有当你需要复用部分参数时才值得用
- 性能:多层嵌套会有微小开销,但对日常开发影响可忽略
- 可读性:过度柯里化会让代码难懂,适度使用
一句话记住
柯里化 = 把一次性点餐,改成自助餐分次取菜
先选主食(固定参数),再选配菜(变化参数),最后吃(执行)。
入门阶段,掌握上面这些就够了。等你熟悉后,可以去了解:
- 自动柯里化(lodash 的
_.curry) - 占位符(跳过某个参数)
- 与偏应用的区别(前面聊过)
