Как решать проблему потери контекста методов в классах JavaScript/TypeScript
Что проверяют
Понимаете ли вы, что this в JavaScript определяется местом вызова, а не местом объявления метода. Следом обычно спрашивают, чем стрелочное поле класса отличается от bind в конструкторе и что происходит при наследовании.
Как отвечать
Покажите, где контекст теряется: метод передают как колбэк (addEventListener('click', this.onClick), setTimeout(this.tick, 100), onClick={this.handleClick} в классовом компоненте React) или деструктурируют const { save } = service. Функция вызывается без объекта слева от точки, а тело класса всегда в строгом режиме, поэтому this равен undefined и код падает с TypeError.
Способы решения:
- стрелочное поле класса:
handleClick = () => {...}, стрелка берётthisлексически. Минус: функция создаётся на каждый экземпляр и лежит не в прототипе, поэтому наследник не вызовет её черезsuper.handleClick(), а шпион на прототипе в тестах её не перехватит; bindв конструкторе:this.handleClick = this.handleClick.bind(this). Метод остаётся в прототипе и доступен наследникам, но кода больше;- стрелка в месте вызова:
() => this.save(). Просто, но новая функция на каждый рендер ломает мемоизацию дочернего компонента; - декоратор
@bound: с TypeScript 5.0 работают стандартные декораторы, и привязка пишется черезcontext.addInitializer.
TypeScript помогает поймать ошибку до запуска: параметр this: Foo в сигнатуре функции, флаг noImplicitThis (входит в strict) и правило @typescript-eslint/unbound-method, которое ругается на передачу непривязанного метода.
Где спотыкаются
Вызывают bind прямо в addEventListener, а потом не могут снять обработчик: каждый bind возвращает новую функцию, и removeEventListener получает другую ссылку. Привязанную функцию нужно сохранить.
Называют поля классов возможностью ES6. В стандарт они вошли в ES2022, до этого их давали Babel и TypeScript.
Говорят, что у стрелочных функций «нет this». Своего нет, но они берут его из окружающей области, поэтому стрелка как метод объектного литерала получит внешний this, а не сам объект.