Какие способы передачи контекста исполнения функции существуют в JavaScript?
Что проверяют
Понимаете ли вы, что this в JavaScript определяется в момент вызова, а не в момент объявления функции. Вопрос базовый, но по нему хорошо видно, знает ли человек язык или заучил три метода. Следом почти всегда спрашивают, что произойдёт, если метод объекта передать в setTimeout или в addEventListener.
Как отвечать
Опишите правила связывания по приоритету:
new: вызов конструктора создаёт новый объект и подставляет его какthis;- явное связывание:
fn.call(thisArg, a, b),fn.apply(thisArg, [a, b]),fn.bind(thisArg, a)возвращает новую функцию с уже зафиксированным контекстом, и повторныйbindеё не перепривяжет; - вызов как метода:
obj.fn()даётobj, ноconst f = obj.fn; f()уже нет. Контекст живёт в точке вызова, а не внутри функции; - обычный вызов: в строгом режиме и в ES-модулях
thisравенundefined, вне строгого режима подставляетсяglobalThis.
Отдельно про стрелки: своего this у них нет, они берут его из лексического окружения, и call, apply, bind на них не влияют. Отсюда два рабочих приёма для колбэков: привязка в конструкторе this.handle = this.handle.bind(this) либо поле класса handle = () => {}. Тело класса всегда исполняется в строгом режиме, поэтому оторванный метод даёт undefined и падает на первом же обращении к полю, а не тихо пишет в глобальный объект.
Полезные детали: в обработчике DOM this равен элементу, на котором висит слушатель, то есть event.currentTarget; forEach, map и some принимают thisArg вторым аргументом; обращение к приватному полю #x с чужим this бросает TypeError.
Где спотыкаются
Объявляют метод объекта стрелочной функцией и удивляются, что this.name даёт undefined. Стрелка взяла контекст модуля, а не объекта.
Считают, что bind меняет исходную функцию. Он возвращает новую, а старая остаётся как была, и забытое присваивание превращается в тихую ошибку.
Вызывают bind прямо в разметке при каждом рендере. Функция каждый раз новая, ссылочное сравнение ломается, а вместе с ним и мемоизация дочерних компонентов.