Подтвердить что ты не робот

Использование функции .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) { ... })

Но оба этих метода чувствуют себя грязными. Любые идеи?

4b9b3361

Ответ 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, хотя, он почти идентичен.