Мануал прошёл шесть сценариев вызова функции плюс стрелочные функции как особый случай. К этому моменту стоит закрепить общую mental model — способ думать про this, который избавляет от угадываний.

Правильный вопрос про this

Начинающие программисты, увидев this в коде, обычно спрашивают:

Откуда берётся this?

Этот вопрос не работает — на него нет однозначного ответа. this не «живёт где-то» и не «приходит откуда-то». Он не свойство функции, не свойство объекта, не глобальная переменная. Правильный вопрос звучит иначе:

Как вызывается функция?

Поменяв вопрос, ты автоматически получаешь ответ. Функция вызвана просто? this — глобальный объект. Через obj.method()? this — сам obj. Через .call(x)? thisx. И так далее.

Для стрелочной функции вопрос другой:

Чем является this внутри внешней функции, где определена эта стрелочная?

Стрелочная функция сама по себе никогда не «получает» this при вызове — она заимствует его из места, где её объявили.

Правило конфликта

Cheatsheet по всем шести сценариям вынесен в обложку мануала — там компактный список. Здесь — про случай, когда сценарии могут одновременно казаться подходящими. Что делать, если функция получена через .bind(), но вызвана через new? Или если .call() применяется к стрелочной функции?

Приоритетность фиксирована и работает так — от сильнее к слабее:

  1. Стрелочная функция побеждает всех. this лексический, .call(), .bind(), new его не меняют. Точка.
  2. new Fn() побеждает .bind(): даже если функция была связана через boundFn = fn.bind(x), вызов new boundFn() создаст новый объект и назначит this ему, игнорируя привязку.
  3. .bind() побеждает .call()/.apply(): как только функция связана, повторное .call(otherContext) уже не переопределит this.
  4. .call()/.apply() побеждают «метод объекта»: obj.method.call(other) установит this = other, а не obj.
  5. «Метод объекта» побеждает «простой вызов»: obj.fn() имеет более сильный сигнал, чем просто fn().

Один взгляд на код — и по этой лестнице сразу понятно, какое правило применять.

Типичные ошибки и лекарства

Из шести сценариев в реальном коде постоянно ловят три типовые ошибки:

  1. Отделили метод от объекта — потеряли this. const fn = obj.method; fn();this уже не obj. Лекарство: fn.bind(obj) или стрелочная-обёртка () => obj.method().
  2. Обычная функция во внутреннем скоупе. Метод объекта содержит обычную функцию, внутри которой ждут прежний контекст. Лекарство: стрелочная функция вместо обычной. См. подглаву 2.3.
  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 продолжают быть популярными на собеседованиях как показатель понимания языка на уровне выше базового.

Основная мысль остаётся простой: смотри не на определение функции, а на её вызов. Всё остальное — следствие.