Frontender Magazine

Частичное применение в JavaScript с помощью bind()

В JavaScript есть один паттерн, который, как я считаю, используется намного реже чем стоило бы. Он даёт более лаконичный код, который проще писать и читать. Рискну предположить, что вы уже слышали о Function.prototype.bind. Этот метод используется, в большинстве случаев, когда необходимо избавиться от чрезмерных присваиваний вроде var that = this или var self = this, встречающихся в коде на каждом шагу. Вот простой пример:

this.setup = function () {
   this.on('event', this.handleEvent.bind(this));
};

Первый аргумент, переданный методу, служит в качестве this в рамках функции, которую возвращает bind. Менее известным качеством bind является то, что он принимает более одного параметра. Каждый следующий после первого параметр bind добавляется в начало списка параметров при вызове привязанной функции.

Это значит, что мы можем создать частичное применение функции следующего вида:

var add = function (a, b) {
  return a + b;
};
var add2 = add.bind(null, 2);

add2(10) === 12;

Увлекательно, правда? Область применения патерна будет более очевидной, если немного расширить изначальный пример обработки события. Ещё один популярный паттерн при обработке событий — добавление какого-либо контента при вызове обработчика:

this.setup = function () {
  this.on('tweet', function (e, data) {
    this.handleStreamEvent('tweet', e, data);
  }.bind(this));
  this.on('retweet', function (e, data) {
    this.handleStreamEvent('retweet', e, data);
  }.bind(this));
};

Если обработчики событий для tweet и retweet используют большую долю общей логики, хорошей идеей будет структурировать код подобным образом. Однако, налицо и побочные эффекты. Например, большое количество шаблонного кода. В обоих случаях придётся создать сопутствующую анонимную функцию, вызывать в ней обработчик события, передать некие параметры, и не забыть привязать к ней контекст this должным образом.

А нельзя ли это сделать как-то проще? Можно конечно!

this.setup = function () {
  this.on('tweet', this.handleStreamEvent.bind(this, 'tweet'));
  this.on('retweet', this.handleStreamEvent.bind(this, 'retweet'));
};

Красота, не правда ли? Итак, что здесь произошло? Вместо того, чтобы вызывать функцию в анонимной обёртке, мы создали две частично применённые функции, которые принимают контекст this и два разных первых параметра каждая. Аргументы e и data будут переданы без нашего вмешательства. Если вы хоть каплю похожи на меня несколько месяцев назад, то в данный момент вы, вероятно, оправились от легкого шока и, уже собираетесь идти подчищать свой код от всех подобных явлений. Когда закончите, не забудьте поделиться моим советом с друзьями.

Если вы заметили ошибку, вы всегда можете отредактировать статью, создать issue или просто написать об этом Антону Немцеву в skype ravencry.

Pascal Hartig
Автор:
Pascal Hartig
GitHub:
http://passy.me/
Twitter:
@passy
Сaйт:
http://passy.me/
Наталья Фадеева
Переводчик:
Наталья Фадеева
вКонтакте:
natatik_l
Twitter:
@very_busy_girl
GitHub:
NatalieF