Частичное применение в 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
будут переданы без нашего вмешательства. Если вы хоть
каплю похожи на меня несколько месяцев назад, то в данный момент вы, вероятно,
оправились от легкого шока и, уже собираетесь идти подчищать свой код от всех
подобных явлений. Когда закончите, не забудьте поделиться моим советом с
друзьями.