为什么在这个 curry 函数的实现中需要使用 bind

Why using bind is necessary in this implementation of curry function

有一个来自 https://www.30secondsofcode.org/js/s/curry 的 curry 函数。

const curry = (fn, arity = fn.length, ...args) => {
  if (args.length >= arity) {
    return fn(...args)
  } else {
    return curry.bind(null, fn, arity, ...args) // line(*)
  }
}
console.log(curry(Math.pow)(2)(10)) // 1024
console.log(curry(Math.min, 3)(10)(50)(2)) // 2

为什么在第(*)行需要使用bind?可以换成

return curry(fn, arity, ...args) // line(**)

?

使用 Function#bind 的原因只有两个:

  1. 当您想将函数绑定到特定上下文对象时。实际上,当您想提前确保函数内的 this 关键字将引用特定的已知对象时。
  2. 当你想预定义一些参数时,a.k.a。 “部分功能应用程序”。

第二种情况完美地解释了柯里化函数的意义——使一个 N 元函数可以灵活调用,例如:

  • fn(arg1, arg2, ..., argN),或
  • fn(arg1, arg2, ...)(argN),或
  • fn(arg1, arg2)(..., argN),或
  • fn(arg1)(arg2)(...)(argN).

这里要注意的重要一点是 我们需要多个单独的函数 用于除第一个以外的所有情况。假设您有一个带有 3 个参数的辅助函数。你可以...

  1. ...为 worker 函数传递足够的参数,即 3 个或更多。然后 curry() 使用这 3 个参数调用 worker 函数并 return 结果。
    这使得 fn(arg1, arg2, arg3) 成为可能。
  2. ...为辅助函数传递的参数太少。然后 curry() 不调用 worker,但必须 return 一个新函数 接受剩余数量的参数。
    这使得所有 fn(arg1)(arg2, arg3)fn(arg1, arg2)(arg3)fn(arg1)(arg2)(arg3) 成为可能。

Function#bind 涉及第二种情况:它为 worker 创建一个新的包装函数,并用给定的值预填充一些 worker 的参数槽。上下文对象与该意图无关,因此在这里使用 null 没问题。

const curry = (fn, arity = fn.length, ...args) =>
    //  enough arguments ?    call     :   remember arguments so far & defer 
    args.length >= arity ? fn(...args) : curry.bind(null, fn, arity, ...args);

所以回答这个问题:不,你不能使用 return curry(fn, arity, ...args) 因为这没有做最重要的事情,即创建一个新函数。


示例: 假设我们有一个辅助函数,用于搜索字符串和 return 命中数。

const search = (str, substr) => {
    var hits = 0, pos = 0;
    while (true) {
        pos = str.indexOf(substr, pos) + 1;
        if (pos) hits++; else return hits;
    }
}

现在我们可以创建一个为我们记住目标字符串的柯里化版本,我们只需要切换最后一个参数:

const flexibleSearch = curry(search);

const reusableSearch = flexibleSearch("... a ... b ... c ... a ... b ... a");

reusableSearch("a")   // -> 3
reusableSearch("b")   // -> 2
reusableSearch("c")   // -> 1
reusableSearch("d")   // -> 0 

@Tomalak 是正确的

假设您有一个带有 100 个参数的函数 fn(1,2,3,...100) {}

fn.bind(null, 99, 100) 使用 预设前导参数 创建一个新函数。在我们的示例中,将使用参数 1,2,...97,98 的默认值,并期望两个参数作为将用作参数 99 和 100 的新函数的输入。

在这种情况下,我们柯里化一个接受部分参数的函数。

你可以通过这些来获得更深入的理解 Wiki for curryingMDN docs for binding partial functions