3个参数的柯里化
// 原函数(3个参数)
const sum = (a, b, c) => a + b + c;
// 柯里化后
const curriedSum = (a) => (b) => (c) => a + b + c;
curriedSum(1)(2)(3); // 6(分三步接收参数)
定义柯里化
// 原函数:计算三个数的和
function add(a, b, c) {
return a + b + c;
}
// 柯里化后的函数
function curriedAdd(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}
// 使用方式
console.log(curriedAdd(1)(2)(3)); // 输出:6
由于闭包的特点可以分步执行
// 通用柯里化工具函数(利用闭包分步收集参数)
function curry(func) {
// 获取原函数的参数长度
const arity = func.length;
// 返回一个闭包函数,用于分步收集参数
return function curried(...args) {
// 当已收集的参数数量达到或超过原函数所需参数时,执行原函数
if (args.length >= arity) {
return func.apply(this, args);
} else {
// 否则返回一个新的闭包函数,继续收集剩余参数
return function(...nextArgs) {
// 将新参数与之前收集的参数合并,递归调用curried继续判断
return curried.apply(this, [...args, ...nextArgs]);
};
}
};
}
// 示例:原函数接收3个参数
const sum = (a, b, c) => a + b + c;
// 柯里化后
const curriedSum = curry(sum);
// 分步收集参数(每次调用返回闭包函数,通过闭包保存已收集的参数)
const step1 = curriedSum(1); // 闭包1:保存a=1,等待b和c
//这时候可以干点别的事
const step2 = step1(2); // 闭包2:保存a=1, b=2,等待c
//这时候可以干点别的事
const result = step2(3); // 执行:1 + 2 + 3 = 6
console.log(result); // 6
// 也可以一次性收集部分参数
console.log(curriedSum(1, 2)(3)); // 6(先收集a=1,b=2,再收集c=3)
console.log(curriedSum(1)(2, 3)); // 6(先收集a=1,再收集b=2,c=3)
console.log(curriedSum(1, 2, 3)); // 6(一次性收集所有参数)
柯里化(Currying)是函数式编程中的重要技术,其核心是将多参数函数转换为一系列单参数函数的链式调用。在实际开发中,柯里化的价值主要体现在参数复用、延迟执行、函数组合等场景,以下是具体应用场景及示例:
1. 参数复用:减少重复传递固定参数
当函数需要频繁使用相同的前置参数时,柯里化可以提前 “固化” 这些参数,生成复用性更高的新函数,避免重复传参。
示例:日志工具
假设需要一个日志函数,需指定日志级别(如info
、error
)和模块名,再传入具体消息。柯里化后可先固定级别和模块,后续只需传递消息
// 普通多参数函数
function log(level, module, message) {
console.log(`[${level}] [${module}] ${message}`);
}
// 柯里化处理
function curryLog(level) {
return function(module) {
return function(message) {
console.log(`[${level}] [${module}] ${message}`);
};
};
}
// 固化 "error" 级别和 "UserModule" 模块
const errorUserLog = curryLog("error")("UserModule");
// 后续调用只需传消息,复用前置参数
errorUserLog("登录失败"); // [error] [UserModule] 登录失败
errorUserLog("权限不足"); // [error] [UserModule] 权限不足
2. 延迟执行:分步收集参数
某些场景需要分步获取参数(如用户操作分步触发),柯里化可将函数执行延迟到所有参数收集完成后,灵活应对动态参数场景。
示例:表单验证
表单验证可能需要先定义验证规则(如长度、格式),再在用户输入时传入具体值进行验证:
// 柯里化验证函数:先传规则,再传值
function curryValidate(rule) {
return function(value) {
switch(rule.type) {
case "minLength":
return value.length >= rule.value;
case "email":
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
}
};
}
// 1. 先定义规则(分步收集第一步参数)
const validateEmail = curryValidate({ type: "email" });
const validatePassword = curryValidate({ type: "minLength", value: 6 });
// 2. 用户输入时验证(分步收集第二步参数)
console.log(validateEmail("test@example.com")); // true
console.log(validatePassword("12345")); // false
3. 函数组合:简化复杂逻辑拼接
柯里化后的函数更容易参与 “函数组合”(将多个函数拼接为一个新函数),这在函数式编程中尤为重要,可实现逻辑的模块化拆分与重组。
示例:数据处理管道
假设有一系列数据处理函数(如过滤、映射、排序),柯里化后可组合成一个处理管道:
// 柯里化工具函数:将多参数函数转为柯里化函数
const curry = (fn) => (...args) =>
args.length >= fn.length ? fn(...args) : (...rest) => curry(fn)(...args, ...rest);
// 数据处理函数(柯里化)
const filter = curry((condition, arr) => arr.filter(condition));
const map = curry((fn, arr) => arr.map(fn));
const sort = curry((comparator, arr) => [...arr].sort(comparator));
// 组合处理管道:过滤偶数 → 乘以2 → 降序排序
const processData = (arr) =>
sort((a, b) => b - a)(
map(x => x * 2)(
filter(x => x % 2 === 0)(arr)
)
);
console.log(processData([1, 2, 3, 4, 5])); // [8, 4]
4. 框架 / 库中的应用
许多现代框架或库会利用柯里化简化 API 设计:
- React 事件处理:需传递额外参数时,柯里化可避免使用
bind
或匿名函数(减少性能损耗):javascript
运行
const handleDelete = (id) => (event) => { // 同时使用 id(业务参数)和 event(事件对象) api.deleteItem(id); }; // 组件中使用 <button onClick={handleDelete(123)}>删除</button>
- Redux 中间件:如
redux-thunk
允许 Action 创建器返回函数,柯里化可方便地注入dispatch
和getState
:const fetchData = (url) => (dispatch) => { dispatch({ type: "FETCH_START" }); fetch(url).then(res => dispatch({ type: "FETCH_SUCCESS", data: res })); };
总结
柯里化的核心价值是 **“分解参数传递”**,通过将多参数逻辑拆解为单参数步骤,提升代码的复用性、灵活性和可读性。它特别适合函数式编程风格,或需要分步处理参数、组合复杂逻辑的场景。但需注意:过度柯里化可能导致代码嵌套过深,需结合实际场景权衡使用。