关于JavaScript通用柯里化函数中绑定函数参数长度的疑问
我在阅读一篇介绍JavaScript通用柯里化实现的文章时,遇到了如下代码:
function curry(fn) { return (...xs) => { if (xs.length === 0) { throw Error('EMPTY INVOCATION'); } if (xs.length >= fn.length) { return fn(...xs); } return curry(fn.bind(null, ...xs)); }; }
我无法理解文中的这段解释:
我们创建一个绑定了前k个参数的fn副本,将其传给curry作为下一个fn,其参数长度(arity)减少为N-k。
想请教:在后续调用中,fn的参数长度为何会减少为N-k?绑定了k个参数的函数的参数长度难道不应该是k吗?
嘿,这个疑问太常见了,我来给你掰扯明白~
首先得搞懂一个关键:JavaScript里函数的length属性,指的是函数定义时声明的形参个数(也就是函数期待接收的参数数量,专业点叫“arity”),可不是调用时传的实参数量,也不是已经绑定的参数数量。
然后看代码里的fn.bind(null, ...xs):这是JavaScript bind方法的特殊特性——当你用bind给原函数绑定了k个前置参数后,返回的新函数的length值,会自动变成原函数的形参个数减去已经绑定的参数个数(也就是N - k)。
举个直观的例子:
假设原函数是:
function add(a, b, c) { return a + b + c; }
它的add.length是3,因为定义时声明了3个形参。
如果我们用bind绑定第一个参数:
const addWithFirst = add.bind(null, 1);
此时addWithFirst.length的值是2——因为这个新函数现在只需要接收剩下的2个参数(b和c)就能完整调用了,它的形参个数变成了3-1=2。
回到柯里化的代码逻辑:
当你第一次调用curry返回的函数时,传入了k个参数(且k < 原fn的length),代码会用bind把这k个参数绑定到原fn上,生成一个新函数。这个新函数的length就是原fn的length减去k(也就是N-k),然后把这个新函数传给下一次的curry调用。
这样下一次调用柯里化后的函数时,判断xs.length >= fn.length里的fn.length,就是这个新函数需要的剩余参数个数,直到传入的参数总数凑够原函数的形参数量,就会执行原函数返回结果。
所以你之前的误解点在于:绑定了k个参数的函数,它的length不是已经绑定的k个,而是还需要接收的剩余参数个数,也就是N-k。这正是bind方法的设计逻辑,也是这段柯里化代码能正常工作的关键~
内容的提问来源于stack exchange,提问作者Varun Hegde

