可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
.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.
回答2:
The following is a common snippet of code I use in all my projects:
var bind = Function.bind;
var call = Function.call;
var bindable = bind.bind(bind);
var callable = bindable(call);
The bindable
function can now be used to pass an array to bind
as follows:
var bound = bindable(db.find, db).apply(null, arguments);
In fact you can cache bindable(db.find, db)
to speed up the binding as follows:
var findable = bindable(db.find, db);
var bound = findable.apply(null, arguments);
You can use the findable
function with or without an array of arguments:
var bound = findable(1, 2, 3);
Hope this helps.
回答3:
Felix's answer didn't work for me because the arguments
object isn't really an array (as Otts pointed out). The solution for me was to simply switch bind
and apply
:
bound = db.find.apply.bind(db.find, null, arguments);
回答4:
For those using ES6, Babel compiles:
db.find.bind(this, ...arguments)
to:
db.find.bind.apply(db.find, [this].concat(Array.prototype.slice.call(arguments)));
I think it's fair to say Babel is pretty definitive. Credit to @lorenz-lo-sauer though, it's almost identical.
回答5:
Why not simply bind to the arguments array as per your example, and have the bound()
function treat it just like that, as an array?
By the looks of your usage, you are then passing in a function as the final argument to bound()
, which means by passing in the actual argument array, you avoid having to separate arguments from callbacks inside bound()
, potentially making it easier to play with.
回答6:
Generically, this schema suffices:
//obj = db
//fnName = 'find'
var args = [this||null].concat(Array.prototype.slice.apply(arguments);
obj[fnName].bind.apply(obj[fnName], args);
回答7:
I find following cleaner than the accepted answers
Function.bind.apply(db.find, [null].concat(arguments));
回答8:
If someone is looking for an abstract sample:
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
}
回答9:
Just had an alternative idea, partially apply the null
value for context and then use apply
to call the partially applied function.
bound = db.find.bind.bind(null).apply(null, arguments);
This removes the need for the slightly spooky looking [null].concat()
in @Felix's answer.
回答10:
A definitive and simple answer might be
Function.apply.bind(this.method, this, arguments);
Kinda "hard" to grasp, yet, neat.