【问题标题】:Can somebody help me write and understand the partial application pattern?有人可以帮助我编写和理解部分应用程序模式吗?
【发布时间】:2015-06-29 14:15:54
【问题描述】:

我正在尝试编写一个利用回调的函数来模仿这种行为:

var fullName = function (firstName, lastName) {
  return firstName + ' ' + lastName;
};

var michaelName = partial(fullName, 'Michael');
michaelName('Moore'); // 'Michael Moore'
michaelName('Jackson'); // 'Michael Jackson'

我被困了一段时间,试图编写一个名为partial 的函数,该函数将回调函数fullName,但最终让它工作但我不确定它是否a)正确和b)到底发生了什么。

var partial = function(callback, firstName) {
    var innerFunction = function(lastName) { //I declared an inner function in order to create a closure of the variable lastName
      return callback(firstName, lastName); //The closure is completed (or is it only then created?) by returning the function argument named callback taking firstName and lastName as arguments
    };
    return innerFunction; //the function partial will finally return the closure, function innerFunction
}

var michaelName = partial(fullName, 'Michael');
console.log(michaelName('Moore')); //Michael Moore

我在定义函数partial 时所写的是编写回调的一般模式吗?如果没有,有人可以向我展示/指出我可以在哪里学习这种模式吗?有没有其他方法可以重构这个回调,让它更干净?我不确定是否需要创建一个新的innerFunction,但是当我这样做时,我终于让行为按预期工作了。我在 cmets 中对代码的推理是否准确?有人介意单步执行我的代码并进一步解释正在发生的事情,为什么callbackinnerFunction 返回以及为什么partial 返回innerFunction 而不是callback?非常感谢。

【问题讨论】:

    标签: javascript function functional-programming higher-order-functions partial-application


    【解决方案1】:

    是的,你写的 partial 函数完全正确。

    我会在这里使用“回调”一词来避免混淆。该词通常在不同的上下文中使用:例如,表示在完成某事时调用的例程,或者由forEach 为数组的每个元素调用的例程。在这里,您传入的只是一个底层函数,当调用返回的函数时,它将与一个额外的指定参数一起被调用。你也可以直接返回函数,所以只要

    var partial = function(fn, firstName) {
        return function(lastName) {
            return fn(firstName, lastName);
        };
    }
    

    还有其他方法可以重构这个回调,让它更干净吗?

    不是真的,除了上面提到的。但是,它可以更通用(见下文)。

    我不确定是否需要创建一个新的innerFunction,但当我这样做时,我终于让行为按预期工作了。

    根据定义,partial 需要返回一个函数。这就是它的目的。所以是的,你必须创建你要返回的函数。您可以将它创建为一个单独的命名函数,然后像您一样将其返回,或者在 return 语句中直接创建它,如上所述。

    我在 cmets 中对代码的推理是否准确?

    不完全是:

    我声明了一个内部函数来创建变量lastName的闭包

    但实际上这个函数正在关闭firstName,来自外部作用域的变量。

    通过返回名为 callback 的函数参数(以 firstName 和 lastName 作为参数)来完成闭包(或者它才创建?)

    对于闭包的确切含义,不同的人有不同的看法。有人会说,仅使用外部范围的变量定义函数就构成了闭包。其他人会说它不是闭包,或者至少称它为闭包没有意义,除非函数被“导出”(返回)以供从外部使用,就像你正在做的那样。

    有人介意单步执行我的代码并进一步解释发生了什么,为什么innerFunction 返回callback 以及为什么partial 返回innerFunction 而不是callback

    callback 没有从innerFunction 返回; 调用的结果就是它。这正是您需要它工作的方式:您希望partial 返回的函数调用您最初传入的函数。直接从partial 返回callback 不会完成任何事情;你只会返回你传入的函数。

    所以整体流程是:

    1. 您有一些像 fullName 这样的函数,并且您想创建另一个函数,该函数的第一个参数“已填充”。

    2. 定义函数partial,它接受一个函数来填充参数,以及要填充的参数。

    3. partial“记住”要调用的函数和传递给它的参数。它返回一个函数(这是你想要的)

    4. 您调用从partial 返回的部分函数,​​该函数使用预先填充的参数调用原始函数,并且将另一个参数传递给该函数。该函数(你的innerFunction)可以“看到”要调用的函数(你称之为callback)和要填写的参数,因为它关闭了它们(这是一个访问这些变量的闭包外部范围。

    但是,您的部分有点有限。一方面,它只处理一个预填充的参数。你可能想处理更多。此外,它不知道如何通过thisinnerFunction。您可以通过以下方式解决这些问题:

    function partial(fn, ...prefilledArgs) {
        return function(...additionalArgs) {
            return fn.call(this, ...prefilledArgs, ...additionalArgs);
        };
    }
    

    在上面,我使用了新的 ES6“传播参数”功能,因为它更容易读写。在 ES5 中,需要使用数组函数将预填充的参数和附加参数拼接在一起,这样玩的并不多,如下:

    function partial(fn)
        var prefilledArgs = Array.prototype.slice.call(arguments, 1);
        return function() {
            var additionalArgs = Array.prototype.slice.call(arguments);
                return fn.apply(this, prefilledArgs.concat(additionalArgs));
        };
    }
    

    在上面,我们“记住”预填充的参数并将它们分配给变量prefilledArgs,方法是使用slice 删除参数伪数组的第一个元素(fn)。然后,在内部函数内部,我们将预先填充的参数和附加参数连接起来,并使用apply 将整个列表传递给原始函数,这是在数组中有参数时调用函数的方式。

    【讨论】:

      【解决方案2】:

      如果您正在寻找一种更简洁的方式来编写代码,我建议您使用 Javascript 闭包。这是解释closures 的链接。

         var partial = function( firstName) {
             return function(lastName) { 
                return firstName + " " + lastName
              };
          }
      
          var michaelName = partial('Michael');
          console.log(michaelName('Moore'));
      

      如果您需要更好地了解回调,Youtube 有一些很棒的视频,这里有一个堆栈帖子帮助我理解 Callbacks

      文章引用

      用简单的英语来说,回调函数就像一个 Worker,它“调用 完成任务后返回给他的经理。

      【讨论】:

      • 你没有抓住重点。他试图将一个函数(在本例中为fullName)转换为一个部分函数。不仅仅是写一个生成器。
      • 是的 @slebetman,我不需要将 partial 写成生成器——我想使用 fullName 来编写 partial 函数。我的做法对吗?
      • 好的,抱歉我现在明白了。这个link 可能会帮助你。
      【解决方案3】:

      按照我的理解,您的模式是正确的,尽管您的 cmets 不太正确。这是一个更通用的实现:

      var partial = function(fn) {
          var appliedArgs = Array.prototype.slice.call(arguments,1);
          return function() {
              var moreArgs = Array.prototype.slice.call(arguments);
              return fn.apply(this,appliedArgs.concat(moreArgs));
          }
      }
      

      这个和你的主要区别在于它允许传入的函数(callback,在你的)有任意数量的参数,并允许部分应用程序满足任意数量的那些......这么长因为它们是按顺序应用的。

      (请注意,MDN docs 建议不要以这种方式对 arguments 进行切片......同时还提供了一个这样做的示例。没有双重信息!)

      好的,让我们逐步介绍您的示例:

      首先,您声明您的基本函数 - fullName()。那里没有混乱。这个函数有两个参数。部分应用的用处在于,您可能会发现自己现在知道一些参数,但直到以后才知道其他参数。部分应用程序允许您立即应用一些参数(因此是“部分应用程序”),返回一个与您的基本函数具有相同效果的函数,但需要更少的参数(因为您已经提供了其他参数)。

      接下来,您调用var michaelName = partial(fullname,"Michael"); 您在这里要求的是“给我一个名为michaelName 的新函数,如果第一个参数始终为'Michael' 和fullName我只需要给它姓”。一旦你理解了这一点,剩下的代码就开始有意义了。

      partial() 必须返回一个函数,而不是一个简单的值,因为这就是您所要求的 - 您传入的回调的新的、更简单的版本。该函数应该做什么?如果使用“Michael”以及给出的任何第二个函数调用,它应该给出回调的结果。

      这又是你的实现:

      var partial = function(callback, firstName) {
          var innerFunction = function(lastName) { 
            return callback(firstName, lastName);
          };
          return innerFunction; 
      }
      

      这正是innerFunction() 的含义。这是一个将已知的firstName 和新的lastName 输入callback() 然后返回结果的函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-09-04
        • 1970-01-01
        • 2017-12-09
        • 2017-02-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多