前端面试 P018:手写柯里化与参数个数边界
来源:方格前端讲面试 · 18. 手写柯里化函数,16 分 49 秒。
回看定位:06:04 参数收集 · 12:31 length 的原说法 · 14:13 部分应用区别
实现思路
用闭包收集参数,每次调用创建新的参数列表;达到约定的 arity 才调用原函数。不要所有分支共用一个不断 push 的数组,否则从同一个部分应用派生的调用可能互相污染。
function curry(fn, arity = fn.length) {
if (typeof fn !== 'function') throw new TypeError('Expected function');
function collect(previous) {
return function (...next) {
const args = [...previous, ...next];
return args.length >= arity
? Reflect.apply(fn, this, args)
: collect(args);
};
}
return collect([]);
}
const sum = curry((a, b, c) => a + b + c);
console.log(sum(1)(2)(3)); // 6
console.log(sum(1, 2)(3)); // 6
const plus1 = sum(1);
console.log(plus1(2, 3)); // 6
console.log(plus1(4, 5)); // 10
这是支持分组收集的实用版本;经典柯里化更强调单参数函数序列。本实现的 this 采用最后一次调用的 this,不会自动永久保留第一步方法接收者,应根据业务明确策略。
需要纠正:函数 length 与解构
视频说 length 不计算解构参数,这不正确。每个解构参数模式通常算一个参数;length 只计算第一个带默认值参数之前的参数,剩余参数不计入。
console.log((function (a, { b }) {}).length); // 2
console.log((function (a, b = 1, c) {}).length); // 1
console.log((function (...items) {}).length); // 0
因此不能把 fn.length 当作函数业务上必需参数数量的绝对真值。带默认值、rest 或约定特殊结束条件时,应显式传 arity 或设计终止调用。
其他边界
超过 arity 的参数,本实现会一并传给目标;零参数调用会继续等待,除非 arity 已满足。占位符、参数位置跳过、构造函数、异步执行规则均不是这个基础版本自动支持的功能。