名词(术语)了解–柯里化(Currying)
柯里化的定义
柯里化是一种将接受多个参数的函数转换成一系列使用单个参数的函数的技术。
这个概念是以逻辑学家 Haskell Curry 命名的。
让我们通过一个简单的例子来说明:
- 普通函数:
function add(x, y, z) {
return x + y + z;
}
console.log(add(1, 2, 3)); // 输出: 6
- 柯里化后的函数:
function curriedAdd(x) {
return function(y) {
return function(z) {
return x + y + z;
}
}
}
console.log(curriedAdd(1)(2)(3)); // 输出: 6
柯里化的优点
- 参数复用:
const addTen = curriedAdd(10);
console.log(addTen(2)(3)); // 15
console.log(addTen(5)(7)); // 22
-
延迟执行:可以等到收集完所有参数后再执行。
-
函数组合:更容易实现函数组合。
实现通用的柯里化函数
下面是一个通用的柯里化函数实现:
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return function(...moreArgs) {
return curried.apply(this, args.concat(moreArgs));
}
};
}
// 使用示例
function sum(a, b, c) {
return a + b + c;
}
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3)); // 6
console.log(curriedSum(1, 2)(3)); // 6
console.log(curriedSum(1)(2, 3)); // 6
实际应用场景
- 事件处理:
const handleChange = curry((name, event) => {
console.log(name, event.target.value);
});
// 可以预设参数
const handleUserNameChange = handleChange('username');
input.addEventListener('change', handleUserNameChange);
- 数据处理:
const map = curry((fn, arr) => arr.map(fn));
const multiply = curry((x, y) => x * y);
const multiplyBy2 = map(multiply(2));
console.log(multiplyBy2([1, 2, 3])); // [2, 4, 6]
- 配置函数:
const ajax = curry((url, method, data) => {
// 发送请求
});
const getFromAPI = ajax('api.example.com');
const getUser = getFromAPI('GET');
const postUser = getFromAPI('POST');
// 使用
getUser({id: 1});
postUser({name: 'John'});
扩展:
实际示例
基础示例
// 普通函数调用 add(1, 2, 3); // 6 // 柯里化函数调用 curriedAdd(1)(2)(3); // 6 // 也可以分步调用 const add1 = curriedAdd(1); const add1and2 = add1(2); const result = add1and2(3); // 6
实用示例
// 日志记录器 const log = (type) => (message) => { console.log(`[${type}] ${message}`); } const errorLog = log('ERROR'); const infoLog = log('INFO'); errorLog('发生错误'); // [ERROR] 发生错误 infoLog('普通信息'); // [INFO] 普通信息
柯里化的优势
参数复用
- 可以固定部分参数,创建新的函数
- 减少重复代码
延迟执行
- 不需要立即给出所有参数
- 可以等到真正需要时再传入剩余参数
函数组合
- 更容易实现函数组合
- 提高代码的模块化程度
通用柯里化函数实现
function curry(fn) { return function curried(...args) { // 如果传入的参数个数大于等于原函数的参数个数,直接调用原函数 if (args.length >= fn.length) { return fn.apply(this, args); } // 否则返回一个新函数,等待接收剩余参数 return function(...args2) { return curried.apply(this, args.concat(args2)); } }; } // 使用示例 function add(x, y, z) { return x + y + z; } const curriedAdd = curry(add); console.log(curriedAdd(1)(2)(3)); // 6 console.log(curriedAdd(1, 2)(3)); // 6 console.log(curriedAdd(1)(2, 3)); // 6
实际应用场景
事件处理
const handleChange = (fieldName) => (event) => { setState({ [fieldName]: event.target.value }); } // 使用 <input onChange={handleChange('username')} /> <input onChange={handleChange('password')} />
数据处理
const filter = (predicate) => (array) => array.filter(predicate); const isEven = n => n % 2 === 0; const filterEven = filter(isEven); console.log(filterEven([1, 2, 3, 4])); // [2, 4]
注意事项
-
性能考虑:柯里化会创建多个闭包,可能会带来轻微的性能开销。
-
代码可读性:过度使用柯里化可能会降低代码的可读性。
-
调试难度:柯里化的函数可能会使调试变得更复杂。
最佳实践
-
在适当的场景使用柯里化
- 参数复用频繁的场景
- 需要延迟执行的场景
- 函数组合的场景
-
保持函数的纯度
- 避免在柯里化函数中使用副作用
- 保持函数的可预测性