← назад к разделу

this — один из самых частых источников неожиданных багов в JavaScript. В большинстве языков this — просто ссылка на текущий объект. В JavaScript всё иначе: значение this определяется не тем, где написана функция, а тем, как она вызвана. Одна и та же функция при разных вызовах получит разный this.

Четыре правила определения this

1. Вызов как метод объекта. this — объект слева от точки.

const user = {
  name: "Алиса",
  greet() {
    console.log(this.name); // "Алиса"
  },
};
user.greet();

2. Обычный вызов функции. this — глобальный объект (window в браузере) или undefined в строгом режиме.

function greet() {
  console.log(this); // window или undefined
}
greet();

3. call, apply, bind. Явное задание this.

function greet(greeting) {
  console.log(`${greeting}, ${this.name}`);
}
const user = { name: "Алиса" };

greet.call(user, "Привет");      // "Привет, Алиса"
greet.apply(user, ["Привет"]);   // то же, аргументы массивом
const bound = greet.bind(user);
bound("Привет");                 // "Привет, Алиса"

4. Конструктор (new). this — новый создаваемый объект.

function User(name) {
  this.name = name;
}
const alice = new User("Алиса");
console.log(alice.name); // "Алиса"

Строгий режим: this без объекта

В нестрогом режиме обычный вызов функции даёт this === window. В строгом ("use strict") — this === undefined. Модули ES и тело класса всегда работают в строгом режиме.

"use strict";
function greet() {
  console.log(this); // undefined, не window
}
greet();

Стрелочные функции: лексический this

Стрелочные функции не имеют собственного this. Они захватывают его из окружающего лексического контекста — из того места, где стрелочная функция написана, а не откуда вызвана.

const counter = {
  count: 0,
  start() {
    setInterval(() => {
      this.count++; // this = counter, захвачен из start()
      console.log(this.count);
    }, 1000);
  },
};
counter.start();

Если заменить стрелку на обычную функцию — this внутри setInterval станет window (или undefined), и count потеряется.

Потеря this при передаче метода как колбэка

Это классическая ловушка. Передача метода как колбэка «отрывает» его от объекта.

const user = {
  name: "Алиса",
  greet() {
    console.log(this.name);
  },
};

setTimeout(user.greet, 100); // undefined — this потерян
document.querySelector("button").addEventListener("click", user.greet); // undefined

Метод передаётся как голая функция и вызывается без объекта слева — срабатывает правило 2, а не правило 1.

Способы фиксации:

// bind — создаёт новую функцию с зафиксированным this
setTimeout(user.greet.bind(user), 100);

// стрелочная обёртка — this берётся лексически
setTimeout(() => user.greet(), 100);

this в классах

В классах методы работают по правилу метода объекта, но при передаче в колбэк — те же грабли. Решение: стрелочное свойство класса.

class Button {
  label = "Нажми меня";

  // Обычный метод — this теряется при передаче как колбэка
  handleClick() {
    console.log(this.label); // может быть undefined
  }

  // Стрелочное свойство — this зафиксирован навсегда
  handleClickArrow = () => {
    console.log(this.label); // всегда "Нажми меня"
  };
}

this в React-компонентах

В классовых компонентах потеря this в обработчиках была повседневной проблемой: нужно было вызывать this.handleClick = this.handleClick.bind(this) в конструкторе или писать стрелочные свойства.

В функциональных компонентах проблемы с this нет совсем. Обработчики событий пишутся как замыкания — переменные берутся из области видимости функции, а не через this.

function Counter() {
  const [count, setCount] = React.useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      {count}
    </button>
  );
}

Это одна из причин, по которым функциональные компоненты стали стандартом.

Ловушки: что выведет код

const obj = {
  val: 42,
  getVal() { return this.val; },
};

const fn = obj.getVal;
console.log(fn());          // undefined (this = window/undefined)
console.log(obj.getVal());  // 42
console.log(fn.call(obj));  // 42
const obj = {
  val: 1,
  outer() {
    const inner = function () {
      return this.val; // this потерян — обычная функция
    };
    return inner();
  },
};
console.log(obj.outer()); // undefined — стрелка вместо function исправит

Коротко

  • this определяется способом вызова, а не местом объявления функции.
  • Четыре правила: метод (obj.fn()) → объект; обычный вызов → window/undefined; call/apply/bind → заданный объект; new → новый объект.
  • В строгом режиме (модули, классы) обычный вызов без объекта даёт undefined, не window.
  • Стрелочные функции не имеют своего this — берут его лексически из окружающего кода.
  • Передача метода как колбэка — потеря this. Фиксируют через .bind() или стрелочную обёртку.
  • В функциональных React-компонентах this не нужен: обработчики работают как замыкания.

Что почитать дальше

  • Основы JavaScript для TypeScript — замыкания и область видимости, фундамент для понимания this.
  • Event loop: как работает и где стреляет — setTimeout, колбэки и порядок выполнения.
  • События DOM: всплытие, погружение, делегирование — где потеря this встречается на практике чаще всего.