Метод bind() в JavaScript
Привязка функций в JavaScript: почему методы теряют `this` при передаче в setTimeout или как колбэк, как работает func.bind(), частичное применение и отличия bind от стрелочных функций.
В JavaScript значение this внутри метода определяется в момент вызова функции, а не в момент её написания. Стоит отделить метод от объекта — например, передав его как колбэк или в setTimeout — и он забудет, к какому объекту принадлежал. Привязка функции — это способ решить проблему: она навсегда фиксирует this на выбранном объекте, так что метод ведёт себя одинаково независимо от места вызова.
В этой главе рассматривается, когда и почему теряется this, как func.bind() решает эту проблему, как использовать bind для частичного применения и чем bind отличается от стрелочных функций.
Проблема «потери this»
Метод работает корректно при вызове в форме object.method(), так как this устанавливается в объект слева от точки. Но если передать тот же метод куда-то ещё, точки уже нет — и this теряется.
Второй вызов выводит Hello, undefined!, потому что runLater вызвал callback() как обычную функцию — без объекта перед точкой, поэтому this уже не является user. (В строгом режиме this равно undefined, и обращение к this.firstName вызвало бы TypeError.)
То же самое происходит с setTimeout — он сохраняет функцию и вызывает её позже самостоятельно, без привязки к какому-либо объекту:
Подробнее о том, как определяется this внутри методов, читайте в разделе Методы объекта, «this».
Решение с помощью func.bind()
Метод func.bind(thisArg) возвращает новую функцию, в которой this навсегда установлен в thisArg. Исходная функция не изменяется — вы получаете связанную копию.
Распространённый паттерн — сохранить привязанную версию обратно в объект, чтобы любую ссылку на неё можно было безопасно передавать:
Привязка происходит только один раз
После привязки функции this фиксируется навсегда. Повторный вызов bind на результате ничего не даёт — вторая привязка игнорируется.
Частичное применение: предустановка аргументов
bind принимает аргументы после thisArg, и они передаются в исходную функцию перед любыми последующими аргументами. Это позволяет создавать специализированные функции из более общих — такой подход называется частичным применением.
Можно одновременно предустановить и this, и аргументы:
bind и стрелочные функции
Стрелочная функция не имеет собственного this — она берёт this из окружающего лексического контекста в момент написания (лексический this). Это делает стрелочные функции лёгкой альтернативой bind, когда нужно сохранить внешний this внутри колбэка:
Как выбрать подходящий вариант:
- Используйте стрелочную функцию, чтобы захватить текущий
thisвстроенно — особенно в колбэках, где не нужна отдельная переиспользуемая функция. - Используйте
bind, когда нужна самостоятельная функция с фиксированнымthisдля передачи в другое место, или когда требуется также предустановить аргументы. В отличие от стрелочной функции, привязанную функцию можно сохранить, переиспользовать и передавать по имени.
Важная оговорка: стрелочную функцию нельзя использовать как метод объекта, в котором this должен указывать на сам объект — она захватывает this из вышестоящего контекста (зачастую глобального объекта), а не от вызывающего кода.
Когда это используется?
- При передаче метода в обработчик событий,
setTimeout/setInterval,Promise.thenили такие методы array какforEach. - При создании специализированных функций из общих с помощью частичного применения (например,
bind(null, presetArg)). - Всякий раз, когда метод должен продолжать работать после отделения от своего объекта.
Если нужно вызвать функцию с заданным this немедленно (а не создавать переиспользуемую копию), используйте call/apply — см. Декораторы и переадресация, call/apply. Более подробно о this в стрелочных функциях читайте в разделе Стрелочные функции: пересмотр.