3

以下代码摘自 Jon Resig 的《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);
          };
        };

编辑:我很困惑,因为args并且arguments在这里必须相同,因为调用之后args = Array.prototype.slice.call(arguments); args是一个真正的数组对象,其中包含arguments. 所以如果里面有东西undefinedargs我们怎么可能有东西在里面arguments呢?

4

3 回答 3

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函数的所有参数,并在 if 之前退出arg >= arguments.length

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

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

 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);
      };
 };
于 2013-06-16T14:14:00.893 回答
1

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

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

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

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

上面的行填充了使用部分时未定义的未定义参数。

例子:-

函数 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
于 2013-06-16T14:19:11.370 回答
0

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

外功能产生外功能arguments(一次),内功能产生内功能arguments(一次或多次)。

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

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

只是在下次到达的基础上进行累积。

在每个内部调用fn.apply(this, args)可能会或可能不会成功执行。fn 将在完全知道它会柯里化的情况下编写。通常,

  • 对于一组不完整的参数 fn 将返回 say undefined(or nullor falseor -1)
  • 对于一组完整的参数,fn 将返回一个有意义的结果。
于 2013-06-16T15:41:03.873 回答