использовать функции.прототип.связывание с массивом аргументов?


Как я могу вызвать функцию.прототип.связывание с массивом аргументов, в отличие от жестко закодированных аргументов? (Не использует ECMA6, поэтому нет оператора распространения).

Я пытаюсь поместить оболочку обещаний вокруг модуля, который использует обратные вызовы, и я хочу связать все аргументы, переданные в мой метод оболочки, и связать их. Затем я хочу вызвать частично примененную привязанную функцию с моим собственным обратным вызовом, который разрешит или отклонит обещание.

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) { ... })
Но оба эти метода кажутся грязными. Есть идеи?
10 44

10 ответов:

.bind это нормальная функция, поэтому вы можете вызвать .apply на нем.
Все, что вам нужно сделать, это передать исходную функцию в качестве первого парама и требуемую переменную THIS в качестве первого элемента в массиве аргументов:

bound = db.find.bind.apply(db.find, [null].concat(arguments));
//      ^-----^            ^-----^   THIS
Можно ли считать это более чистым или нет, остается за читателем.

Ниже приведен общий фрагмент кода, который я использую во всех своих проектах:

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

Надеюсь, это поможет.

Ответ Феликса не сработал для меня, потому что объект arguments на самом деле не является массивом (как указывал ОТТС). Решением для меня было просто переключить bind и apply:

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

Для тех, кто использует ES6, Babel компилирует:

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

Кому:

db.find.bind.apply(db.find, [this].concat(Array.prototype.slice.call(arguments)));
Я думаю, будет справедливо сказать, что Вавилон довольно категоричен. Хотя, надо отдать должное @lorenz-lo-sauer, он почти идентичен.

Почему бы просто не привязать к массиву аргументов, как в вашем примере, и заставить функцию bound() обращаться с ним точно так же, как с массивом?

Судя по тому, как вы используете функцию, вы затем передаете ее в качестве конечного аргумента в bound(), что означает, что, передавая фактический массив аргументов, вы избегаете необходимости отделять аргументы от обратных вызовов внутри bound(), что потенциально облегчает игру.

В общем случае этой схемы достаточно:

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

Я нахожу следующие более чистые, чем принятые ответы

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

Если кто-то ищет абстрактный образец:

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
}

Просто была альтернативная идея, частично применить значение null для контекста, а затем использовать apply для вызова частично применяемой функции.

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

Это устраняет необходимость в слегка жутковатом взгляде [null].concat() в ответе @Felix.

Окончательный и простой ответ может быть

Function.apply.bind(this.method, this, arguments);

Вроде "трудно" понять, но все же аккуратно.