Метод функций bind своими руками

Основы основ

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

var sum = function(a, b) {
  return this.sum + a + b;
};

Изначально, при запуске в глобальной области видимости функция будет использовать объект window в качестве контекста, поэтому если попробовать её вызвать без задания контекста, вы получите не самый очевидный рельтат:

// Вызов функции, контекстом является window
sum(1, 2);

// Результат выполнения
"function (a, b) {
  return this.sum + a + b;
}12"

При инициализации переменной sum с помощью оператора var происходит запись анонимной функции в глобальную область видимости (объект window в браузере). Именно поэтому результатом выполнения функции является такое странное значение. Работает это следующим образом:

  1. При создании переменной sum в глобальной области видимости в объект window записывается соответствующее свойство.
  2. При выполнении функции контекстом является объект window, то есть this ссылается на window.
  3. При использовании оператора сложения будет произведено автоматическое приведение типов

Чтобы указать функции sum контекст выполнения воспользуемся метод bind:

var bindedSum = sum.bind({
  sum: 10
});

bindedSum(1, 2); // 13

Метод bind вернул новую функцию, привязанную к контексту обозначенного объекта. Сама функция sum при этом не была изменена.

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

var sum = function() {
  return [].reduce.call(arguments, function(result, current) {
    return result + current;
  }, this.sum);
};

// Привязка контекста и нескольких аргуметов
var bindedSum = sum.bind({ sum: 10 }, 20, 30);
bindedSum(40, 50); // 10 + 20 + 30 + 40 + 50 === 150

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

Bind своими руками

Вы получили хитрое задание: вам необходимо реализовать метод bind самостоятельно в виде функции, сам метод bind, естественно, использовать нельзя. Не паникуйте, у вас ещё остались методы call и apply. Для начала, обозначим себе три правила:

  1. Функция bind принимает неограниченное количество аргументов, но первый и второй отвечают за саму функцию, которой будет задаваться контекст, и сам контекст соответственно. Все остальные агрументы должны быть переданы в функцию.
  2. Функция bind не вызывает передаваемую ей функцию, а лишь задаёт для неё контекст.
  3. Получаемой при помощи bind функции можно передать неограниченное количество аргументов, которые будут использованы по мере надобности.
var bind = function(fn, context) {
  // обрезаем ненужные аргументы (функцию и контекст)
  var bindArgs = [].slice.call(arguments, 2);
  return function() {
    // здесь все аргументы будут необходимы
    var fnArgs = [].slice.call(arguments);
    // собираем все 
    return fn.apply(context, bindArgs.concat(fnArgs));
  };
};

Написанная выше функция bind работает точно также, как и метод bind:

var bindedSum = bind(sum, {sum: 10}, 20, 30);
bindedSum(40, 50, 60); // 210

Комментарии