web-dev-qa-db-ja.com

Function.prototype.bindを引数の配列で使用していますか?

ハードコーディングされた引数ではなく、引数の配列を使用してFunction.prototype.bindを呼び出すにはどうすればよいですか? (ECMA6を使用していないため、スプレッド演算子はありません)。

コールバックを使用するモジュールの周りにpromisesラッパーを配置しようとしています。ラッパーメソッドに渡されたすべての引数をバインドし、それらをバインドします。次に、部分的に適用されたバインドされた関数を自分のコールバックで呼び出します。これにより、約束が解決または拒否されます。

var find = function() {
  var deferred, bound;
  deferred = Q.defer();
  bound = db.find.bind(null, arguments);
  bound(function(err, docs) {
    if(err) {
      deferred.fail(err);
    } else {
      deferred.resolve(docs);
    }
  });
  return deferred.promise;
}

しかし、バインドは引数の配列ではなく引数を要求するため、明らかにこれは機能しません。引数配列の最後にコールバックを挿入し、applyを使用することでこれができることを知っています。

arguments[arguments.length] = function(err, docs) { ... }
db.find.apply(null, arguments);

または、arguments配列を反復処理して、各引数の関数を再バインドします。

var bound, context;
for(var i = 0; i < arguments.length; i++) {
   context = bound ? bound : db.find;
   bound = context.bind(null, arguments[i]);
}
bound(function(err, docs) { ... })

しかし、これらの方法はどちらも汚い感じがします。何か案は?

47
Dan Prince

.bindは通常の関数なので、.apply.
最初のパラメータとして元の関数を渡し、引数の配列の最初の項目として目的のTHIS変数を渡すだけです。

bound = db.find.bind.apply(db.find, [null].concat(arguments));
//      ^-----^            ^-----^   THIS

それがきれいであると考えられるかどうかは読者に任されています。

78
Felix Kling

以下は、すべてのプロジェクトで使用するコードの一般的なスニペットです。

_var bind = Function.bind;
var call = Function.call;

var bindable = bind.bind(bind);
var callable = bindable(call);
_

bindable関数を使用して、次のように配列をbindに渡すことができます。

_var bound = bindable(db.find, db).apply(null, arguments);
_

実際、次のようにバインディングを高速化するためにbindable(db.find, db)をキャッシュできます。

_var findable = bindable(db.find, db);
var bound = findable.apply(null, arguments);
_

findable関数は、引数の配列の有無にかかわらず使用できます。

_var bound = findable(1, 2, 3);
_

お役に立てれば。

11
Aadit M Shah

argumentsオブジェクトは実際には配列ではないため、Felixの答えはうまくいきませんでした(Ottsが指摘したように)。私にとっての解決策は、単にbindapplyを切り替えることでした:

bound = db.find.apply.bind(db.find, null, arguments);
9
Joel

ES6を使用している場合、Babelは次をコンパイルします。

db.find.bind(this, ...arguments)

に:

db.find.bind.apply(db.find, [this].concat(Array.prototype.slice.call(arguments)));

バベルはかなり決定的だと言ってもいいと思います。しかし、@ lorenz-lo-sauerに感謝します。ほとんど同じです。

4
nathancahill

あなたの例のように単純に引数配列にバインドし、bound()関数にそれを配列としてのように処理させるのはなぜですか?

使用方法の見た目では、bound()の最後の引数として関数を渡します。つまり、実際の引数配列を渡すことで、bound()、プレイしやすくする可能性があります。

1
Matt Way

受け入れられた答えよりも次のことがわかりました

Function.bind.apply(db.find, [null].concat(arguments));
1
brillout

誰かが抽象的なサンプルを探している場合:

var binded = hello.apply.bind(hello,null,['hello','world']);

binded();

function hello(a,b){
  console.log(this); //null
  console.log(a); //hello
  console.log(b); //world
}
1
Paweł

一般的に、このスキーマで十分です。

//obj = db
//fnName = 'find'
var args = [this||null].concat(Array.prototype.slice.apply(arguments);
obj[fnName].bind.apply(obj[fnName], args);
1
Lorenz Lo Sauer

別のアイデアがあり、コンテキストにnull値を部分的に適用し、applyを使用して部分的に適用された関数を呼び出します。

_bound = db.find.bind.bind(null).apply(null, arguments);
_

これにより、@ Felixの回答で少し不気味に見える[null].concat()の必要がなくなります。

0
Dan Prince

決定的な簡単な答えは

Function.apply.bind(this.method, this, arguments);

ちょっと「難しい」とはいえ、きちんとしている。

0
131