18 августа 2019 г.

Позднее связывание "bindLate"

Материал на этой странице устарел, поэтому скрыт из оглавления сайта.

Обычный метод bind называется «ранним связыванием», поскольку фиксирует привязку сразу же.

Как только значения привязаны – они уже не могут быть изменены. В том числе, если метод объекта, который привязали, кто-то переопределит – «привязанная» функция этого не заметит.

Позднее связывание – более гибкое, оно позволяет переопределить привязанный метод когда угодно.

Раннее связывание

Например, попытаемся переопределить метод при раннем связывании:

function bind(func, context) {
  return function() {
    return func.apply(context, arguments);
  };
}

var user = {
  sayHi: function() { alert('Привет!'); }
}

// привязали метод к объекту
var userSayHi = bind(user.sayHi, user);

// понадобилось переопределить метод
user.sayHi = function() { alert('Новый метод!'); }

// будет вызван старый метод, а хотелось бы - новый!
userSayHi(); // выведет "Привет!"

…Привязка всё ещё работает со старым методом, несмотря на то что он был переопределён.

Позднее связывание

При позднем связывании bind вызовет не ту функцию, которая была в sayHi на момент привязки, а ту, которая есть на момент вызова.**

Встроенного метода для этого нет, поэтому нужно реализовать.

Синтаксис будет таков:

var func = bindLate(obj, "method");
obj
Объект
method
Название метода (строка)

Код:

function bindLate(context, funcName) {
  return function() {
    return context[funcName].apply(context, arguments);
  };
}

Этот вызов похож на обычный bind, один из вариантов которого как раз и выглядит как bind(obj, "method"), но работает по-другому.

Поиск метода в объекте: context[funcName], осуществляется при вызове, самой обёрткой.

Поэтому, если метод переопределили – будет использован всегда последний вариант.

В частности, пример, рассмотренный выше, станет работать правильно:

function bindLate(context, funcName) {
  return function() {
    return context[funcName].apply(context, arguments);
  };
}

var user = {
  sayHi: function() { alert('Привет!'); }
}

var userSayHi = bindLate(user, 'sayHi');

user.sayHi = function() { alert('Здравствуйте!'); }

userSayHi(); // Здравствуйте!

Привязка метода, которого нет

Позднее связывание позволяет привязать к объекту даже метод, которого ещё нет!

Конечно, предполагается, что к моменту вызова он уже будет определён ;).

Например:

function bindLate(context, funcName) {
  return function() {
    return context[funcName].apply(context, arguments);
  };
}

// метода нет
var user = {  };

// ..а привязка возможна!
var userSayHi = bindLate(user, 'sayHi');

// по ходу выполнения добавили метод..
user.sayHi = function() { alert('Привет!'); }

userSayHi(); // Метод работает: Привет!

В некотором смысле, позднее связывание всегда лучше, чем раннее. Оно удобнее и надёжнее, так как всегда вызывает нужный метод, который в объекте сейчас.

Но оно влечёт и небольшие накладные расходы – поиск метода при каждом вызове.

Итого

Позднее связывание ищет функцию в объекте в момент вызова.

Оно используется для привязки в тех случаях, когда метод может быть переопределён после привязки или на момент привязки не существует.

Обёртка для позднего связывания (без карринга):

function bindLate(context, funcName) {
  return function() {
    return context[funcName].apply(context, arguments);
  };
}
Карта учебника

Комментарии

перед тем как писать…
  • Если вам кажется, что в статье что-то не так - вместо комментария напишите на GitHub.
  • Для одной строки кода используйте тег <code>, для нескольких строк кода — тег <pre>, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)
  • Если что-то непонятно в статье — пишите, что именно и с какого места.