功能编程 - .bind.apply用于咖喱功能

时间:2015-02-25 06:54:46

标签: javascript functional-programming currying

Reading about functional programming - 得到了讨论,例子有一个简单的currying功能。我理解除了最后一个else块之外的所有内容。

var curry = function (fn, fnLength) {
    fnLength = fnLength || fn.length;
    return function () {
        var suppliedArgs = Array.prototype.slice.call(arguments);
        if (suppliedArgs.length >= fn.length) {
            return fn.apply(this, suppliedArgs);
        } else if (!suppliedArgs.length) {
            return fn;
        } else {
            return curry(fn.bind.apply(fn, [this].concat(suppliedArgs)), fnLength - suppliedArgs.length);
       }
    };
};

如果提供的参数为>=,请使用提供的参数调用该函数。

如果suppliedArgs.length是假的,则返回原始函数而不做任何事情。

否则???

  • 我认为递归调用函数?
  • 我不明白.bind.apply完成了什么。
  • [this]只是在数组中,因为suppliedArgs.push不会返回数组吗?

3 个答案:

答案 0 :(得分:3)

首先查看您如何致电Function#bind()

  

fun.bind(thisArg [,arg1 [,arg2 [,...]]])

然后考虑如何使用Function#apply()

  

fun.apply(thisArg,[argsArray])

因此给定bind()我们需要在一个函数上调用它,并给它多个参数(不是数组),而我们所拥有的只是一个参数数组(代码中为suppliedArgs)那我们怎么做呢?那么,你可以调用一个带有多个参数而不是一个数组的参数的函数的主要方法是在函数上使用.apply()。那么我们有fn.bind.apply(...something...)

.apply()的第一个参数是this值 - 在.bind()的情况下需要绑定的函数(请参阅下面的解释原因)。因此fn.bind.apply(fn, ...)

然后,.apply()的第二个参数是您要调用的函数的所有参数的数组,在.bind()的情况下是thisArg[, arg1[, arg2[, ...]]]。因此,我们需要一个数组,第一个值是函数中this的值,后跟其他参数。这是[this].concat(suppliedArgs)产生的。

因此整个fn.apply.bind(fn, [this].concat(suppliedArgs))事件产生一个正确绑定的函数,该函数将使用正确的this上下文为当前函数“prefilled”提供参数。然后,生成的此函数将作为fn参数传递给curry()的递归调用,这反过来将以与顶级调用相同的方式生成另一个函数。

总体效果是,无论何时调用由curry()创建的函数,如果未传递预期数量的参数,您将获得一个新函数,该函数将获取剩余的参数数量,或者您将使用正确传递的整个参数列表评估原始函数。

e.g。

function addAllNums(a, b, c, d, e) {
    return a + b + c + d + e;
}

var curriedAddAll = curry(addAllNums, 5);
var f1 = curriedAddAll(1); // produces a function expecting 4 arguments
var f2 = f1(2, 3); // f2 is a function that expects 2 arguments
var f3 = f2(4); // f3 is a function that expects 1 argument
var f4 = f3(5); // f4 doesn't expect any arguments
var ans = f4(); // ans = 1 + 2 + 3 + 4 + 5 = 15.
// OR var ans = f3(5); => same result

为什么不同的thisArg值?

这行代码最令人困惑的可能是thisArg.bind().apply()的两个不同值。

对于.apply()thisArg就是您希望this的值位于您正在调用的函数.apply()内。例如myFunction.apply(myObj, ['param1', 'param2'])相当于myObj.myFunction('param1', 'param2')

在这种特殊情况下,.bind()会在fn函数上执行,因此我们希望fnthis的{​​{1}}值,因此它知道创建绑定版本的函数是什么函数。

对于.bind().bind()thisArg的值将在返回的绑定函数内。

在我们的例子中,我们想要返回一个与我们当前具有相同this值的绑定函数。换句话说,我们希望在新函数中正确维护this值,因此在创建新函数时不会丢失,当您调用参数少于预期的curried函数时会发生这种情况。

如果我们没有正确维护this值,则以下示例不会记录this的正确值。但是通过维护它,将输出正确的值。

this

答案 1 :(得分:0)

最后一个else块是curry函数的主要和最重要的部分,因为它是带有currying逻辑的实际行。

return curry(fn.bind.apply(fn, [this].concat(suppliedArgs)), fnLength - suppliedArgs.length);

这是返回需要来自前一个函数的n-1个参数的新函数。为什么?它是多种因素的组合:

fn.bind.apply只是在函数本身的上下文中调用函数,同时提供所需的args(suppliedArgs)。注意curry的下一个参数是fnLength - suppliedArgs.length,它减少了传递内容所需的参数。

答案 2 :(得分:0)

让我们借助ES6对其进行解释。事情将变得更加明显。

// Imagine we have the following code written in ES5
function fn(a, b, c) {
  console.log(a, b, c);
}

var arr = [1, 2, 3];
var funcWithBoundArguments = fn.bind.apply(fn, [null].concat(arr));

让我们将ES5转换为ES6代码

// ES6
function fn(a, b, c) { console.log(a, b, c) }

let arr = [1,2,3];

let funcWithBoundArguments = fn.bind(null, ...arr)

你看到了吗?当您绑定一个函数时,我们必须显式枚举所有参数,例如:

fn.bind(null, 1, 2, 3)

但是,如果我们事先不知道数组的内容,该如何绑定数组的内容?

好的,我们必须在其中使用.bind.apply()

  • apply的第一个参数是我们绑定的函数(fn
  • 第二个参数是一个数组,该数组获取我们将函数绑定到的上下文(作为数组的第一项),而数组的其余项是我们绑定到函数的参数(该数字是变量) (fn