柯里化函数实现
手写 JavaScript 柯里化 (Curry) 函数
问题
实现一个 curry 函数,将多参数函数转换为可以逐个传参调用的函数。
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
curriedAdd(1)(2)(3); // 6
curriedAdd(1, 2)(3); // 6
curriedAdd(1)(2, 3); // 6
解答
基础实现
function curry(fn) {
return function curried(...args) {
// 如果传入的参数数量 >= 原函数参数数量,直接执行
if (args.length >= fn.length) {
return fn.apply(this, args);
}
// 否则返回一个新函数,继续收集参数
return function (...nextArgs) {
return curried.apply(this, args.concat(nextArgs));
};
};
}
使用示例
// 原函数
function add(a, b, c) {
return a + b + c;
}
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
console.log(curriedAdd(1, 2, 3)); // 6
支持占位符的实现
function curry(fn, placeholder = curry.placeholder) {
return function curried(...args) {
// 检查是否有占位符,以及参数是否足够
const complete = args.length >= fn.length
&& !args.slice(0, fn.length).includes(placeholder);
if (complete) {
return fn.apply(this, args);
}
return function (...nextArgs) {
// 用 nextArgs 填充 args 中的占位符
const merged = args.map(arg =>
arg === placeholder && nextArgs.length ? nextArgs.shift() : arg
);
return curried.apply(this, merged.concat(nextArgs));
};
};
}
// 定义占位符
curry.placeholder = Symbol('placeholder');
占位符使用示例
const _ = curry.placeholder;
function log(a, b, c) {
console.log(a, b, c);
}
const curriedLog = curry(log);
curriedLog(_, 2, 3)(1); // 1 2 3
curriedLog(_, _, 3)(1)(2); // 1 2 3
curriedLog(1, _, 3)(2); // 1 2 3
ES6 简洁写法
const curry = (fn, ...args) =>
args.length >= fn.length
? fn(...args)
: (...nextArgs) => curry(fn, ...args, ...nextArgs);
关键点
fn.length获取函数的形参个数- 参数不足时返回新函数继续收集参数
- 参数足够时调用原函数并返回结果
- 使用
apply或展开运算符传递参数数组 - 占位符实现需要在合并参数时替换占位位置
目录