3

次のコードは、関数の部分的な適用を実装するためにクロージャーを使用する方法を説明するために、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);
          };
        };

編集: と は、呼び出し後に に含まれるすべての情報を保持する真の配列オブジェクトであるため、ここでは必ずしも同じであるためargs、混乱しています。では、何かがの中にある場合、どうすれば の中に何かを入れることができるのでしょうか?argumentsargs = Array.prototype.slice.call(arguments); argsargumentsundefinedargsarguments

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と の値をarguments2 つの変数に格納します。これは、両方の値が次の関数ブロックでオーバーライドされるためです。

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); }

//2 番目の引数に数値 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 に答える