Несколько дней назад меня спросили, могу ли я отличить apply, call и bind. И apply, и call используются для вызова функций, и существуют десятки сообщений, объясняющих, как они это делают. Они ожидают это в качестве своего первого аргумента, и если вы когда-нибудь читали это умное сравнение на Переполнение стека:

Подумайте о применении для массива аргументов и c при вызове столбцов аргументов.

вы вряд ли когда-нибудь забудете, в чем единственная реальная разница. apply ожидает аргументы после этого в виде массива, а call ожидает их по отдельности.

Из-за отсутствия автоматической привязки в React я регулярно использовал bind, но до сих пор понятия не имел, как это по сравнению с apply или call, поэтому здесь мои выводы.

Сходство

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

Разница

apply и call, используемые для вызова функций, bind создает новую функцию. Когда эта функция вызывается, ее ключевое слово this будет установлено на значение, которое вы указали в качестве первого аргумента.

var cat = {
  name: 'Milu',
  greet: function (greeting) {
    return greeting + ', ' + this.name;
  }
};
cat.greet.apply(cat, ['Hi']);                 // returns Hi, Milu cat.greet.call(cat, 'Hello');                 // returns Hello, Milu
var greetBound = cat.greet.bind(cat, 'Hey');  // returns a function greetBound();                                 // returns Hey, Milu

Эти вещи можно найти в документации, поэтому давайте сделаем то, чего там нет.

Вызов с объектами Function

Ниже приведена функция purr, которая на самом деле является объектом Function. Каждый объект Function имеет свойство name, поэтому следующий вызов приведет к:

function purr() {
  return 'purrs at a frequency of 20 to 30 vibrations per second';
}
var greetBound = cat.greet.bind(purr, ['Hey']);
greetBound(); // returns Hey, purr

То же самое произойдет, если вы вызовете apply или call, указав purr в качестве первого аргумента.

Вызов с массивом аргументов

Передача массива аргументов в bind с последующим вызовом связанной функции будет иметь тот же эффект, что и передача его в call:

var cat = {
  name: 'Milu',
  greet: function (greeting) {
    return greeting + ', ' + this.name;
  }
};
cat.greet.call(cat, ['Hi', 'Hello']); // returns Hi,Hello, Milu
var greetBound = cat.greet.bind(cat, ['Hi', 'Hello']);
greetBound();                         // returns Hi,Hello, Milu

bind преобразует свой второй аргумент в строку, прежде чем передать ее связанной функции.

Когда не использовать привязку

bind на самом деле просто вызов, за исключением того, что вызов связанной функции может быть задержан. Это напоминает нам функции обратного вызова. К сожалению, в большинстве примеров, с которыми я сталкивался, они использовались с функциями обратного вызова, но они не имели самого продуманного дизайна, который оправдывал использование bind:

var Widget = function () {
  this.counter = 0;
  $('button').on('click', function () {
    this.counter += 1;
    $('span').text(this.counter);
  }.bind(this));
};

Более простой виджет:

var Widget = function () {
  var counter = 0;
  $('button').on('click', function () {
    counter += 1;
    $('span').text(counter);
  });
};

Просматривая десятки примеров, я увидел, что bind более широко используется там, где код имеет традиционный объектно-ориентированный дизайн — чего я вообще избегал, когда дело доходит до JavaScript, и это в значительной степени объясняет, почему я понятия не имел, как это соотносится с вызовом и применить.

У вас есть конкретный сценарий, в котором bind неизбежен или более уместен? Дай мне знать в комментариях!