this определяется в момент вызова
Большая часть путаницы вокруг this в JavaScript растёт из одного ложного допущения: будто this зависит от того, где функция объявлена. Нет. this зависит от того, как функцию вызвали.
Посмотрите, как одна и та же функция ведёт себя при двух разных вызовах:
Одна и та же функция — разный this. В первом вызове перед точкой стоит user, поэтому this равен user. Во втором вызове перед функцией нет ничего, и this оказывается undefined. Сама функция не изменилась — изменилось место вызова.
Запомните эту мысль: чтобы понять, чему равен this внутри функции javascript, смотрите не на объявление, а на вызов.
Четыре правила привязки this
Привязка this в javascript определяется одним из четырёх правил. Практически любой вопрос про this сводится к тому, чтобы понять, какое именно из них сработало.
1. Вызов как метод: obj.fn()
Если функция вызывается как свойство объекта, this — это сам объект:
То, что стоит перед точкой, и становится this. Вот и вся магия.
2. Обычный вызов: fn()
Если функция вызывается без объекта перед ней, то this будет undefined в strict mode (а он автоматически включается в модулях и классах) или глобальным объектом в нестрогом режиме:
Именно отсюда берутся ошибки вида «this is undefined». Стоит вытащить метод из объекта — и вызов метода превращается в обычный вызов функции:
Нет counter. перед вызовом — нет и привязки. Функция попросту не помнит объект, из которого её достали.
3. Явная привязка: .call(), .apply(), .bind()
this можно насильно указать вручную — в какое угодно значение:
.call и .apply вызывают функцию сразу же и отличаются только тем, как передаются аргументы. А вот .bind возвращает новую функцию, в которой this зафиксирован намертво — удобно для колбэков.
4. Вызов через new: new Fn()
Когда функция вызывается через new, создаётся новый объект, и именно он становится this:
Классы используют этот же механизм под капотом. Мы познакомимся с ними в одной из следующих глав.
this в стрелочной функции
Стрелочные функции намеренно ломают правила, о которых мы говорили выше. У них вообще нет собственной привязки this — они подхватывают его из окружающего контекста, причём фиксируют в момент объявления стрелки:
Стрелочная функция на верхнем уровне модуля захватывает this самого модуля, а это undefined. И хотя мы вызываем её как user.arrow(), стрелка наотрез отказывается перепривязываться.
На первый взгляд похоже на баг, но в этом и весь смысл. this в стрелочной функции особенно удобен внутри методов — когда нужно сохранить внешний this:
Стрелочная функция внутри setInterval наследует this от start, а start был вызван как timer.start(). Поэтому this.seconds работает как надо. Обычная function на этом месте получила бы собственный this (тот, который подсунет setInterval), и всё бы сломалось.
Правило на практике: для колбэков внутри методов берите стрелочные функции, а сами методы объявляйте как обычные функции.
Классическая ловушка с this в колбэке
Это самый частый способ потерять this в javascript. Передаёте метод как колбэк — и привязка слетает:
setTimeout вызывает функцию как обычную, а не как c.increment(). Исправить это можно тремя способами:
Все три варианта рабочие. Но обёртка со стрелочной функцией обычно читается понятнее всего.
this на верхнем уровне
Значение this на верхнем уровне зависит от того, где выполняется код:
- Скрипт в браузере (не модуль):
this— этоwindow. - ES-модуль (сюда же относится большинство современного кода, собранного бандлером):
thisравенundefined. - CommonJS-модуль в Node.js:
this— этоmodule.exports.
Если нужна надёжная ссылка на глобальный объект в любом окружении, используйте globalThis:
На практике лучше не полагаться на this верхнего уровня. Если вам действительно нужен глобальный объект — берите globalThis. Во всех остальных случаях передавайте значения явно.
Быстрое дерево решений
Если вы смотрите на this и не понимаете, чему он будет равен, пройдитесь по этому списку:
- Это стрелочная функция? Тогда
thisберётся из внешней области видимости. Как её вызвали — неважно. - Функцию вызвали через
new? Тогдаthis— это новый объект. - Её вызвали через
.call,.applyили как связанную (bound) функцию? Тогдаthis— то, что передали. - Вызов вида
obj.method()? Тогдаthis— этоobj. - Обычный вызов
fn()? Тогда в strict modethisбудетundefined.
Эта лесенка — именно в таком порядке — покрывает все случаи.
Дальше: функции высшего порядка
Теперь, когда this больше не загадка, можно переходить к приёму, за счёт которого JavaScript такой выразительный — к передаче функций как обычных значений. В следующей главе разберём функции высшего порядка: те, что принимают другие функции или возвращают их. Именно на них держатся методы массивов, обработчики событий и, по сути, весь живой JavaScript-код.
Часто задаваемые вопросы
На что указывает this в JavaScript?
this в JavaScript?this указывает на объект, у которого вызвана функция, а вовсе не туда, где она была объявлена. В вызове user.greet() this — это user. А если просто написать greet(), то в strict mode this будет undefined (в нестрогом режиме — глобальный объект). Главное правило: смотрим на место вызова, а не на место объявления.
Почему внутри функции this равен undefined?
this равен undefined?Скорее всего, вы оторвали метод от объекта и вызвали его отдельно — или передали как колбэк. В коде const fn = user.greet; fn(); привязка теряется: слева от точки в месте вызова нет никакого объекта. Лечится через .bind(user), обёртку со стрелочной функцией или просто вызовом user.greet().
Чем this в стрелочных функциях отличается от обычных?
this в стрелочных функциях отличается от обычных?У стрелочных функций нет собственного this — они берут его из окружающей области видимости в момент объявления. Поэтому их удобно использовать в колбэках внутри методов, когда нужно сохранить внешний this. И ещё важный момент: .call(), .apply() и .bind() не могут подменить this у стрелочной функции.
Чему равен this на верхнем уровне скрипта?
this на верхнем уровне скрипта?В обычном скрипте в браузере this на верхнем уровне — это объект window. В ES-модуле — undefined. В CommonJS-модулях Node.js — module.exports. Универсальная ссылка на глобальный объект, работающая везде, — это globalThis.