web-dev-qa-db-ja.com

JavaScript関数呼び出しで引数を事前設定するにはどうすればよいですか? (部分機能アプリ)

最初の引数(関数)を返し、残りのすべての引数をその関数への事前設定パラメーターとして返すJavaScript関数を記述しようとしています。

そう:

function out(a、b){
 document.write(a + "" + b); 
} 
 
 function setter(...){。 ..} 
 
 setter(out、 "hello")( "world"); 
 setter(out、 "hello"、 "world")(); 

「hello world」を2回出力します。セッターのいくつかの実装

最初の試行で引数の配列を操作する際に問題が発生しましたが、これを行うためのより良い方法があるようです。

53
AlexH

まず最初に、パーシャルが必要です- パーシャルとカレーの違いがあります -そして、これがすべてです必要、フレームワークなし

_function partial(func /*, 0..n args */) {
  var args = Array.prototype.slice.call(arguments, 1);
  return function() {
    var allArguments = args.concat(Array.prototype.slice.call(arguments));
    return func.apply(this, allArguments);
  };
}
_

さて、あなたの例を使用して、あなたはあなたが望んでいることを正確に行うことができます:

_partial(out, "hello")("world");
partial(out, "hello", "world")();

// and here is my own extended example
var sayHelloTo = partial(out, "Hello");
sayHelloTo("World");
sayHelloTo("Alex");
_

partial()関数は実装に使用できますが、はカレー化しません違いに関するブログ投稿 からの引用は次のとおりです:

部分的なアプリケーションが関数を取り、それからより少ない引数を取る関数を構築する場合、カリー化は、それぞれが単一の引数を取る関数の組み合わせによって複数の引数を取る関数を構築します。

お役に立てば幸いです。

96
Jason Bunting

curried javascript はあなたが探しているものですか?

3
Peter Bailey

Dojoを使用する場合は、dojo.hitch()を呼び出すだけです。ほぼ—コンテキストのパックにも使用できるため。しかし、あなたの例は最初です:

dojo.hitch(out, "hello")("world");
dojo.hitch(out, "hello", "world")();

と同様:

var A = {
  sep: ", ",
  out: function(a, b){ console.log(a + this.sep + b); }
};

// using functions in context    
dojo.hitch(A, A.out, "hello")("world");
dojo.hitch(A, A.out, "hello", "world")();

// using names in context
dojo.hitch(A, "out", "hello")("world");
dojo.hitch(A, "out", "hello", "world")();

dojo.hitch()はDojo Baseの一部なので、dojo.jsをインクルードするとすぐに使用できます。

Dojox.lang.functional.curryモジュールで別の一般的な機能を利用できます( で説明されているJavaScriptでの機能的な楽しみDojo —このページで「カレー")。具体的には、curry()、partial()を確認することをお勧めします。

curry()は(例のように)引数を蓄積しますが、1つの違いがあります。アリティが満たされるとすぐに、値を返す関数を呼び出します。あなたの例を実装する:

df.curry(out)("hello")("world");
df.curry(out)("hello", "world");

最後の行の最後に "()"がないことに注意してください—自動的に呼び出されます。

partial()は引数をランダムに置き換えることができます:

df.partial(out, df.arg, "world")("hello");
2
Eugene Lazutkin

JavaScriptのapply()を使用すると、_function prototype_を変更できます

_Function.prototype.pass = function() {
    var args = arguments,
        func = this;
    return function() {
        func.apply(this, args);
    }
};
_

次に、それをout.pass('hello','world')として呼び出すことができます。

applyは、2番目の引数/パラメーターの配列を取ります。

argumentsは、構造体のような配列内のすべてのパラメーターを含む関数内で使用可能なプロパティです。

これを行うもう1つの一般的な方法は、bindを使用することです

loadedFunc = func.bind(this, v1, v2, v3);

その後

loadedFunc() === this.func(v1,v2,v3);

少し醜いですが、これで十分です。

2
Muhammad Umer

これには Function.prototype.bind() を使用できます。 ES5の追加です。

関数のコンテキスト(this値)を設定する一般的なユースケースに加えて、部分的な引数を設定することもできます。

function out(a, b) {
  document.write(a + " " + b);
}

function setter(func) {
  return func.bind.apply(func, [window].concat([].slice.call(arguments).slice(1)));
}

setter(out, "hello")("world");
setter(out, "hello", "world")();

私のsetter関数は実際には非常に単純です。最も長い部分は、引数のリストを取得することです。次のようにコードを分割します。

func.bind.apply(func, [window].concat([].slice.call(arguments).slice(1)))
func.bind.apply(                                                        )  // need to use apply to pass multiple arguments as an array to bind()
                func,                                                      // apply needs a context to be run in
                      [window].concat(                                 )   // pass an array of arguments to bind(), starting with window, to be the global context
                                      [].slice.call(arguments).slice(1)    // convert the arguments list to an array, and chop off the initial value

次のブラウザでサポートされています これらのブラウザ :Chrome 7 +、Firefox 4 +、IE9 +。MDN(最初にリンクされています)でもポリフィルがあります。

1
Scimonster

**編集:Jason Buntingの応答を参照してください。この回答は、実際には、いくつかの引数のプリセットを使用した単一の発信ではなく、多数の発信を連鎖させる準標準的な方法を示しています。この答えが実際に同様の問題に役立つ場合は、私が考えたevalを使用するあいまいな方法の代わりに、Jasonが推奨するように、applyおよびcallを必ず使用してください。 **

まあ...あなたのアウトは実際にこれに「未定義」をたくさん書くでしょう...しかしこれはあなたが望むものに近いはずです:

function out(a, b) {
    document.write(a + " " + b);
}

function getArgString( args, start ) {
    var argStr = "";
    for( var i = start; i < args.length; i++ ) {
        if( argStr != "" ) {
            argStr = argStr + ", ";
        }
        argStr = argStr + "arguments[" + i + "]"
    }
    return argStr;
}

function setter(func) {
    var argStr = getArgString( arguments, 1 );
    eval( "func( " + argStr + ");" );
    var newSettter = function() {
        var argStr = getArgString( arguments, 0 );
        if( argStr == "" ) {
            argStr = "func";
        } else {
            argStr = "func, " + argStr;
        }
        return eval( "setter( " + argStr + ");" );
    }
    return newSettter;
}

setter(out, "hello")("world");
setter(out, "hello", "world")();

おそらくgetArgStringのコードをセッター関数自体に移動するでしょうが、 'eval'を使用しているので、少し安全です。

0
Illandril