Domanda Utilizzando Function.prototype.bind con una matrice di argomenti?


Come posso chiamare Function.prototype.bind con una serie di argomenti, al contrario di argomenti hardcoded? (Non si usa ECMA6, quindi nessun operatore di spread).

Sto provando a mettere un wrapper promises attorno a un modulo che utilizza i callback e voglio associare tutti gli argomenti passati al mio metodo wrapper e vincolarli. Quindi voglio chiamare la funzione associata parzialmente applicata con il mio callback, che risolverà o rifiuterà una promessa.

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

Ma ovviamente questo non funziona perché bind si aspetta argomenti piuttosto che una serie di argomenti. So che potrei farlo inserendo la mia callback alla fine dell'array degli argomenti e usando apply:

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

O iterando sulla matrice degli argomenti e riconciliando la funzione per ogni argomento:

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) { ... })

Ma entrambi questi metodi si sentono sporchi. Qualche idea?


44
2018-02-02 05:13


origine


risposte:


.bind è una funzione normale, quindi puoi chiamare .apply su di essa.
Tutto quello che devi fare è passare la funzione originale come prima param e desiderata THIS variabile come primo elemento dell'array di argomenti:

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

Se questo può essere considerato più pulito o meno è lasciato al lettore.


73
2018-02-02 05:39



Quello che segue è uno snippet di codice comune che uso in tutti i miei progetti:

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

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

Il bindable la funzione può ora essere utilizzata per passare un array a bind come segue:

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

In effetti puoi memorizzare nella cache bindable(db.find, db) per accelerare il legame come segue:

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

Puoi usare il findable funzione con o senza un array di argomenti:

var bound = findable(1, 2, 3);

Spero che questo ti aiuti.


10
2018-02-15 03:13



La risposta di Felix non ha funzionato per me perché il arguments l'oggetto non è in realtà un array (come ha sottolineato Otts). La soluzione per me era semplicemente cambiare bind e apply:

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

8
2018-06-06 16:50



Per chi usa ES6, Babel compila:

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

a:

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

Penso che sia giusto dire che Babel è piuttosto definitivo. Ringraziamo @ Lorenz-lo-Sauer, è quasi identico.


3
2018-04-30 05:48



Perché non semplicemente associare alla matrice di argomenti come da esempio, e avere il bound() funzione trattalo proprio così, come una matrice?

Con l'aspetto del tuo utilizzo, stai passando a una funzione come argomento finale bound(), che significa passando l'array di argomenti effettivo, si evita di dover separare gli argomenti dai callback all'interno bound(), potenzialmente facilitando il gioco.


1
2018-02-02 05:35



In generale, questo schema è sufficiente:

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

1
2018-02-05 12:17



Trovo i seguenti pulitori rispetto alle risposte accettate

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

1
2017-11-17 12:19



Se qualcuno sta cercando un campione astratto:

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
2017-08-14 09:27



Ho appena avuto un'idea alternativa, in parte applica il null valore per il contesto e quindi uso apply chiamare la funzione parzialmente applicata.

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

Ciò elimina la necessità di un aspetto un po 'spettrale [null].concat() in @ la risposta di Felix.


0
2018-02-15 02:03