Using Function.prototype.bind with an array of arguments?

Dan Prince picture Dan Prince · Feb 2, 2014 · Viewed 15.3k times · Source

How can I call Function.prototype.bind with an array of arguments, as opposed to hardcoded arguments? (Not using ECMA6, so no spread operator).

I'm trying to put a promises wrapper around a module that uses callbacks and I want to bind all of the arguments passed in to my wrapper method and bind them. Then I want to call the partially applied bound function with my own callback, which will resolve or reject a promise.

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

But obviously this doesn't work because bind expects arguments rather than an array of arguments. I know I could do this by inserting my callback onto the end of the arguments array and using apply:

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

Or by iterating over the arguments array and rebinding the function for each 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) { ... })

But both of these methods feel dirty. Any ideas?

Answer

Felix Kling picture Felix Kling · Feb 2, 2014

.bind is a normal function, so you can call .apply on it.
All you have to do is pass the original function as the first param and the desired THIS variable as the first item in the array of arguments:

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

Whether that can be considered cleaner or not is left to the reader.