Comment appeler Function.prototype.bind avec un tableau d'arguments, par opposition aux arguments codés en dur? (Ne pas utiliser ECMA6, donc pas d’opérateur de propagation).
J'essaie de placer un wrapper de promesses autour d'un module qui utilise des rappels et je veux lier tous les arguments transmis à ma méthode wrapper et les lier. Ensuite, je souhaite appeler la fonction liée partiellement appliquée avec mon propre rappel, ce qui résout ou rejette une promesse.
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;
}
Mais évidemment, cela ne fonctionne pas parce que bind attend des arguments plutôt qu'un tableau d'arguments. Je sais que je pourrais le faire en insérant mon rappel à la fin du tableau d'arguments et en utilisant apply:
arguments[arguments.length] = function(err, docs) { ... }
db.find.apply(null, arguments);
Ou en parcourant le tableau d'arguments et en reliant la fonction pour chaque argument:
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) { ... })
Mais ces deux méthodes se sentent sales. Des idées?
.bind
est une fonction normale, vous pouvez donc appeler .apply
dessus.
Tout ce que vous avez à faire est de passer la fonction d'origine en tant que premier paramètre et la variable THIS
souhaitée en tant que premier élément du tableau d'arguments:
bound = db.find.bind.apply(db.find, [null].concat(arguments));
// ^-----^ ^-----^ THIS
Que cela puisse ou non être considéré comme plus propre est laissé au lecteur.
Voici un extrait de code commun que j'utilise dans tous mes projets:
var bind = Function.bind;
var call = Function.call;
var bindable = bind.bind(bind);
var callable = bindable(call);
La fonction bindable
peut maintenant être utilisée pour passer un tableau à bind
comme suit:
var bound = bindable(db.find, db).apply(null, arguments);
En fait, vous pouvez mettre en cache bindable(db.find, db)
pour accélérer la liaison comme suit:
var findable = bindable(db.find, db);
var bound = findable.apply(null, arguments);
Vous pouvez utiliser la fonction findable
avec ou sans tableau d'arguments:
var bound = findable(1, 2, 3);
J'espère que cela t'aides.
La réponse de Felix n'a pas fonctionné pour moi parce que l'objet arguments
n'est pas vraiment un tableau (comme l'a souligné Otts). La solution pour moi était simplement de changer bind
et apply
:
bound = db.find.apply.bind(db.find, null, arguments);
Pour ceux qui utilisent ES6, Babel compile:
db.find.bind(this, ...arguments)
à:
db.find.bind.apply(db.find, [this].concat(Array.prototype.slice.call(arguments)));
Je pense qu'il est juste de dire que Babel est assez définitif. Merci à @ Lorenz-Lo-Sauer, il est presque identique.
Pourquoi ne pas simplement lier le tableau d’arguments selon votre exemple et laisser la fonction bound()
la traiter comme ça, comme un tableau ?
Selon l'apparence de votre utilisation, vous transmettez ensuite une fonction en tant qu'argument final à bound()
, ce qui signifie qu'en transmettant le tableau d'arguments actuel, vous évitez de séparer les arguments des rappels à l'intérieur de bound()
, ce qui facilite potentiellement la lecture.
Je trouve que les réponses suivantes sont plus propres que celles acceptées
Function.bind.apply(db.find, [null].concat(arguments));
Si quelqu'un recherche un échantillon abstrait:
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
}
Généralement, ce schéma suffit:
//obj = db
//fnName = 'find'
var args = [this||null].concat(Array.prototype.slice.apply(arguments);
obj[fnName].bind.apply(obj[fnName], args);
Vous venez d'avoir une idée alternative, appliquez partiellement la valeur null
au contexte, puis utilisez apply
pour appeler la fonction partiellement appliquée.
bound = db.find.bind.bind(null).apply(null, arguments);
Cela supprime le besoin de [null].concat()
dans la réponse de @ Felix, qui a l'air légèrement sinistre.
Une réponse simple et définitive pourrait être
Function.apply.bind(this.method, this, arguments);
Un peu "difficile" à saisir, mais soigné.