Использование функции .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;
}
Но, очевидно, это не работает, потому что bind ожидает аргументы, а не массив аргументов. Я знаю, что могу сделать это, вставив мой обратный вызов в конец массива аргументов и используя apply:
arguments[arguments.length] = function(err, docs) { ... }
db.find.apply(null, 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) { ... })
Но оба этих метода чувствуют себя грязными. Любые идеи?
Ответы
Ответ 1
.bind
является нормальной функцией, поэтому вы можете называть ее .apply
.
Все, что вам нужно сделать, это передать исходную функцию как первый параметр и желаемую переменную THIS
в качестве первого элемента массива аргументов:
bound = db.find.bind.apply(db.find, [null].concat(arguments));
// ^-----^ ^-----^ THIS
Можно ли считать, что это чище или нет, читателю.
Ответ 2
Ниже приведен общий фрагмент кода, который я использую во всех моих проектах:
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);
Надеюсь, что это поможет.
Ответ 3
Ответ Felix не работал у меня, потому что объект arguments
не является массивом (как отметил Оттс). Решением для меня было просто переключить bind
и apply
:
bound = db.find.apply.bind(db.find, null, arguments);
Ответ 4
Почему бы просто не привязать массив аргументов к вашему примеру и использовать функцию bound()
так просто, как массив?
По взглядам вашего использования вы затем передаете функцию в качестве окончательного аргумента в bound()
, что означает, что, передавая в фактическом массиве аргументов, вы избегаете отделять аргументы от обратных вызовов внутри bound()
, потенциально что облегчает игру.
Ответ 5
В общем случае этой схемы достаточно:
//obj = db
//fnName = 'find'
var args = [this||null].concat(Array.prototype.slice.apply(arguments);
obj[fnName].bind.apply(obj[fnName], args);
Ответ 6
Я нахожу следующий чище, чем принятые ответы
Function.bind.apply(db.find, [null].concat(arguments));
Ответ 7
Если кто-то ищет абстрактный образец:
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
}
Ответ 8
Просто появилась альтернативная идея, частично применив значение null
для контекста, а затем используйте apply
для вызова частично применяемой функции.
bound = db.find.bind.bind(null).apply(null, arguments);
Это устраняет необходимость слегка призрачного поиска [null].concat()
в ответе @Felix.
Ответ 9
Окончательный и простой ответ может быть
Function.apply.bind(this.method, this, arguments);
Своего рода "трудно" понять, тем не менее, аккуратно.
Ответ 10
Для тех, кто использует ES6, Babel компилирует:
db.find.bind(this, ...arguments)
в
db.find.bind.apply(db.find, [this].concat(Array.prototype.slice.call(arguments)));
Я думаю, справедливо сказать, что Бабель довольно определенно. Кредит @lorenz-lo-sauer, хотя, он почти идентичен.