Мануал прошёл шесть сценариев вызова функции плюс стрелочные функции как особый случай. К этому моменту стоит закрепить общую mental model — способ думать про this, который избавляет от угадываний.
Правильный вопрос про this
Начинающие программисты, увидев this в коде, обычно спрашивают:
Откуда берётся this?
Этот вопрос не работает — на него нет однозначного ответа. this не «живёт где-то» и не «приходит откуда-то». Он не свойство функции, не свойство объекта, не глобальная переменная. Правильный вопрос звучит иначе:
Как вызывается функция?
Поменяв вопрос, ты автоматически получаешь ответ. Функция вызвана просто? this — глобальный объект. Через obj.method()? this — сам obj. Через .call(x)? this — x. И так далее.
Для стрелочной функции вопрос другой:
Чем является this внутри внешней функции, где определена эта стрелочная?
Стрелочная функция сама по себе никогда не «получает» this при вызове — она заимствует его из места, где её объявили.
Правило конфликта
Cheatsheet по всем шести сценариям вынесен в обложку мануала — там компактный список. Здесь — про случай, когда сценарии могут одновременно казаться подходящими. Что делать, если функция получена через .bind(), но вызвана через new? Или если .call() применяется к стрелочной функции?
Приоритетность фиксирована и работает так — от сильнее к слабее:
- Стрелочная функция побеждает всех. this лексический, .call(), .bind(), new его не меняют. Точка.
- new Fn() побеждает .bind(): даже если функция была связана через boundFn = fn.bind(x), вызов new boundFn() создаст новый объект и назначит this ему, игнорируя привязку.
- .bind() побеждает .call()/.apply(): как только функция связана, повторное .call(otherContext) уже не переопределит this.
- .call()/.apply() побеждают «метод объекта»: obj.method.call(other) установит this = other, а не obj.
- «Метод объекта» побеждает «простой вызов»: obj.fn() имеет более сильный сигнал, чем просто fn().
Один взгляд на код — и по этой лестнице сразу понятно, какое правило применять.
Типичные ошибки и лекарства
Из шести сценариев в реальном коде постоянно ловят три типовые ошибки:
- Отделили метод от объекта — потеряли this. const fn = obj.method; fn(); — this уже не obj. Лекарство: fn.bind(obj) или стрелочная-обёртка () => obj.method().
- Обычная функция во внутреннем скоупе. Метод объекта содержит обычную функцию, внутри которой ждут прежний контекст. Лекарство: стрелочная функция вместо обычной. См. подглаву 2.3.
- Стрелочная как метод объекта. { method: () => this.something } — this не будет объектом. Лекарство: обычная функция или short-method-syntax. См. подглаву 7.2.
Что дальше в современном коде
Знание этих правил остаётся важным, но в современных практиках проблема this встречается заметно реже:
- Функциональные React-компоненты вместо классов. Хуки (useState, useEffect) не имеют собственного this. С 2019 года новый React-код почти всегда пишется на функциональных компонентах, где вопрос про this просто не возникает.
- Стрелочные функции по умолчанию. В новом коде большинство функций — стрелочные. Обычные function остаются только там, где нужен свой this (методы классов, обработчики событий в некоторых случаях).
- TypeScript с strict-режимом. Компилятор ловит многие ошибки контекста ещё до запуска. Например, strictBindCallApply проверяет корректность типов при вызове через .call()/.apply()/.bind().
- Class fields с arrow-функциями. В классах React и других OO-фреймворков часто используют handleClick = () => { ... } как class-field — стрелочная функция получает this экземпляра и не «теряется» при передаче в event handlers.
Полное понимание this остаётся полезным по двум причинам. Первая — чтение старого кода. Legacy-JS до массового появления стрелочных функций и хуков полон паттернов с this, и понимать их важно даже если сам ты пишешь по-новому. Вторая — интервью. Вопросы про this продолжают быть популярными на собеседованиях как показатель понимания языка на уровне выше базового.
Основная мысль остаётся простой: смотри не на определение функции, а на её вызов. Всё остальное — следствие.