Можно ли забиндить контекст функции в JavaScript и как это сделать
Что проверяют
Понимаете ли вы, что this определяется в момент вызова, а не при объявлении функции, и знаете ли, чем bind отличается от call и apply. Следом почти всегда спрашивают про стрелочные функции и про потерю контекста в колбэках.
Как отвечать
Сначала правило: значение this зависит от того, как функцию вызвали.
- как метод,
obj.method():thisравенobj; - просто функцией,
fn(): в строгом режимеundefined, в нестрогом глобальный объект. Модули и классы всегда в строгом режиме, поэтому во фронтенд-коде это почти всегдаundefined; - через
new: новый объект; - стрелочная функция: своего
thisнет, берётся из окружения, где стрелка создана.
Три способа задать контекст явно:
bind(ctx, ...args): не вызывает функцию, а возвращает новую с навсегда привязаннымthisи, при желании, частично применёнными аргументами;call(ctx, a, b): вызывает сразу, аргументы через запятую;apply(ctx, [a, b]): то же, но аргументы массивом. Сейчас его обычно заменяют наcall(ctx, ...args).
Классическая потеря контекста: setTimeout(user.greet, 1000). Метод передан как значение, и связь с объектом пропадает. Решения: bind, обёртка () => user.greet() или поле класса со стрелкой. В классовых компонентах React ради этого писали this.handleClick = this.handleClick.bind(this) в конструкторе.
Добавьте тонкости: повторный bind уже привязанной функции контекст не меняет, стрелке bind не меняет this вовсе, а при вызове через new привязанный this игнорируется.
Где спотыкаются
Говорят, что по умолчанию this это window. В модулях и строгом режиме там undefined, отсюда классическое Cannot read properties of undefined.
Пытаются привязать контекст к стрелочной функции через bind и не понимают, почему не работает.
Пишут addEventListener('scroll', this.onScroll.bind(this)), а потом не могут снять обработчик: bind каждый раз создаёт новую функцию, и removeEventListener получает другую ссылку.