了解Jon Resig的部分应用程序的实现

时间:2013-06-16 13:40:24

标签: javascript closures partial-application

以下代码摘自Jon Resig的书Secrets of JavaScript Ninja,解释如何使用闭包来实现部分功能应用。但是我在理解变量arg的意图时遇到了问题。为什么需要它以及它如何简化预先填充函数的一些参数的问题?这个partial函数的可能应用是什么?

    Function.prototype.partial = function() {
          var fn = this, args = Array.prototype.slice.call(arguments);
          return function() {
            var arg = 0;   // How does this do the currying
            for (var i = 0; i < args.length && arg < arguments.length; i++) {
              if (args[i] === undefined) {
                args[i] = arguments[arg++]; //This line is where the confusion is
              }
            }
            return fn.apply(this, args);
          };
        };

编辑:我很困惑,因为argsarguments在这里必定相同,因为在调用args = Array.prototype.slice.call(arguments); args之后是一个真正的数组对象,它包含所有包含的信息在arguments。因此,如果undefined中的某些内容为args,那么我们怎样才能在arguments内找到某些内容?

3 个答案:

答案 0 :(得分:3)

好的,我正试图一块一块地解释它:

Function.prototype.partial = function() {
      var fn = this, args = Array.prototype.slice.call(arguments);
      return function() {
        var arg = 0;
        for (var i = 0; i < args.length && arg < arguments.length; i++) {
          if (args[i] === undefined) {
            args[i] = arguments[arg++]; //This line is where the confusion is
          }
        }
        return fn.apply(this, args);
      };
 };

第一行:

var fn = this, args = Array.prototype.slice.call(arguments);

这会将this的值和arguments的值存储在两个变量中,因为这两个值都会在以下功能块中被覆盖:

return function() {
    //inside here arguments will be whatever is passed to this returned function later.
};

for循环:

var arg = 0;
for (var i = 0; i < args.length && arg < arguments.length; i++) {
}

它基本上会遍历传递给partial函数的所有参数,并在arg >= arguments.length之前退出。

if (args[i] === undefined) {
    args[i] = arguments[arg++]; //This line is where the confusion is
}

因此,如果args数组的参数未定义,我们将其替换为arguments数组中的下一个参数。当每个参数被替换时,原始函数将使用merged arguments array调用:

 return fn.apply(this, args);

以下是它在实践中的运作方式:

 function xy(arg1, arg2) {
     console.log(arg1 + " / " + arg2);
 }

 var p1 = xy.partial("foo", undefined);

 p1("bar") //"foo / bar"

 var p2 = xy.partial(undefined, "bar");

 p2("foo") //"foo / bar"

 var p3 = xy.partial("foo");

 p3("bar") //"foo / undefined" --> because you have to activly pass "undefined" otherwise the arguments array is too short

最后但并非最不重要的是,此代码如何使用示例p1 = xy.partial("foo", undefined);详细说明:

//lets call xy.partial("foo", undefined);
Function.prototype.partial = function() {
      //fn = xy
      //args = ["foo", undefined]
      var fn = this, args = Array.prototype.slice.call(arguments);

      //return function that is assigned to p1
      //lets call p1("bar")
      return function() {
        //arguments = ["bar"]
        var arg = 0;
        //for (var i = 0; i < 2 && arg < 1; i++)
        for (var i = 0; i < args.length && arg < arguments.length; i++) {
          //first iteration:
          //args[0] === "foo" -> nothing happend
          //second iteration:
          //args[1] === undefined -> args[1] = arguments[0] (= "bar"); arg++;
          if (args[i] === undefined) {
            args[i] = arguments[arg++]; //This line is where the confusion is
          }
        }
        //at this point: args = ["foo", "bar"];
        //now just call the function with the merged array
        return fn.apply(this, args);
      };
 };

答案 1 :(得分:1)

部分函数可帮助您预先填充函数执行所需的函数参数。这有助于以多种方式减少开销。

上面的例子有助于以后填充未定义的参数。

arg 是一个私有变量,用于跟踪未定义的参数并按顺序填充未定义的参数。

args[i] = arguments[arg++];

上面填充未定义参数的行在使用partial时未定义。

实施例: -

函数a(b,c){    的console.log(B,C); }

//用数值1预先填充第二个参数但是第一个参数仍未定义。这将返回一个新功能。

var g = a.partial(undefined, 1);

//现在,当您实际调用该函数并将其传递给参数时,这将填充先前未定义的参数,即“b”。在这种情况下, var arg 将填充先前的 args 数组位置,这些位置未定义,即args [0]未定义。因此 var arg 将按顺序读取传递的参数并为您填充先前未定义的参数。

g("Hi I was undefined before");

如果不希望 args 永久更改,请按以下步骤更新部分实施。

Function.prototype.partial = function() {
                var fn = this, args = Array.prototype.slice.call(arguments);
                return function() {
                  var arg = 0, g;//g will hold the clone of args
                  g = args.slice(0);//clone the args array and use that instead of original args.
                  for (var i = 0; i < g.length && arg < arguments.length; i++) {
                    if (g[i] === undefined) {
                      g[i] = arguments[arg++];
                    }
                  }

                  return fn.apply(this, g);
                };
            };

function fn(a, b){  }
var fn = f.partial(undefined, 3), x = fn(2), y= fn(4);//Now this will work

答案 2 :(得分:0)

我认为最好用文字解释。至少,我会试一试:

外部函数产生外部arguments(一次),内部函数产生内部arguments(一次或多次)。

同时args是一个“累加器”,其中第一个(外部)调用的参数和所有后续(内部)调用的参数被逐步存储。这一行:

args[i] = arguments[arg++];

只是在 next-to-arrival 的基础上进行累积。

每次内部呼叫fn.apply(this, args)可能会也可能不会成功执行。 fn将完全知道它会被咖喱。通常,

  • 对于不完整的参数设置,fn将返回说undefined(或nullfalse-1
  • 对于完整的参数集fn将返回有意义的结果。