this в JavaScript: контекст вызова и правила

Что такое this в JavaScript?

Что такое this простыми словами

this — это специальное значение (не «переменная в обычном смысле»), доступное внутри функции, которое обычно указывает на объект, через который функция была вызвана. Иначе говоря, this отвечает на вопрос: «какой объект является получателем вызова прямо сейчас».

Важно различать два похожих утверждения:

  • «Функция объявлена внутри объекта» (место объявления)
  • «Функция вызвана как метод объекта» (форма вызова)

this в обычных функциях зависит именно от формы вызова.

Главное правило: this вычисляется при вызове

Для обычных функций (function declaration, function expression, методы объектов и классов) действует принцип: значение this определяется тем, как функция вызвана.

Таблица: как определяется this

Как вызывается функцияПример вызоваКакое значение this
Обычный вызовfn()В нестрогом режиме: глобальный объект (в браузере часто window); в строгом режиме: undefined
Вызов как методobj.fn()obj (объект слева от точки на момент вызова)
Явная установка контекстаfn.call(ctx, a) / fn.apply(ctx, [a])ctx
Привязка контекста навсегдаfn.bind(ctx)Всегда ctx при вызове полученной функции
Конструкторnew Fn()Новый созданный объект
Стрелочная функция() => thisthis берётся из внешнего лексического окружения и не меняется call/apply/bind

Схема принятия решения

Упрощённый порядок проверки для вычисления this:

  1. Если функция стрелочная, то this берётся снаружи (лексически)
  2. Иначе, если вызов с new, то this — новый объект
  3. Иначе, если есть bind, то this — привязанный объект
  4. Иначе, если вызов через call/apply, то this — переданный объект
  5. Иначе, если вызов как obj.method(), то thisobj (то, что слева от точки)
  6. Иначе обычный вызов: this равен undefined в strict mode, либо глобальному объекту в нестрогом режиме

Разбор каждого способа вызова с примерами

Обычный вызов: fn()

В обычном вызове нет объекта-получателя, поэтому this подставляется по правилам режима выполнения.

// Нестрогий режим (пример для браузера)
function g() {
  return this === window;
}
console.log(g()); // true (часто так в браузере, если не модуль и не strict)
// Строгий режим
function s() {
  "use strict";
  return this;
}
console.log(s()); // undefined

Замечание: в современных проектах часто используются ES-модули и строгий режим, поэтому ожидание «this всегда window» является небезопасным.

Вызов как метод: obj.fn()

Если функция вызывается через свойство объекта, то this получает значение объекта слева от точки.

const user = {
  name: "Ирина",
  getName() {
    return this.name;
  }
};

console.log(user.getName()); // "Ирина"

Ключевой момент: важна форма вызова, а не то, где функция была написана. При «отрыве» метода this теряется.

const user = {
  name: "Ирина",
  getName() {
    return this.name;
  }
};

const fn = user.getName;
console.log(fn()); // strict: ошибка (this = undefined), non-strict: может быть window.name

call и apply: явная установка this

call принимает аргументы списком, apply принимает массив аргументов. Оба метода устанавливают this явно.

function sum(a, b) {
  return this.base + a + b;
}

const ctx = { base: 10 };

console.log(sum.call(ctx, 1, 2));    // 13
console.log(sum.apply(ctx, [1, 2])); // 13
Это прямое доказательство, что this не «вшит» в функцию навсегда, а задаётся вызовом.

bind: фиксированная привязка this

bind возвращает новую функцию с заранее привязанным this. Эта привязка не зависит от дальнейшей формы вызова.

function greet() {
  return "Привет, " + this.name;
}

const user = { name: "Олег" };
const bound = greet.bind(user);

console.log(bound()); // "Привет, Олег"
const other = { name: "Наталья" };
console.log(bound.call(other)); // всё равно "Привет, Олег"

new: конструктор создаёт новый this

Вызов с new создаёт новый объект и делает его значением this внутри функции-конструктора.

function Person(name) {
  this.name = name;
}

const p = new Person("Анна");
console.log(p.name); // "Анна"
Вызов функции-конструктора без new часто приводит к некорректному this (в strict mode это обычно undefined, в нестрогом режиме — глобальный объект), поэтому такая запись считается источником ошибок.

Стрелочные функции: this берётся снаружи

Стрелочная функция не создаёт собственный this. Вместо этого она «захватывает» this из внешней области, где была создана.

Это удобно для колбэков внутри методов, когда требуется сохранить контекст объекта.

const counter = {
  value: 0,
  incLater() {
    setTimeout(() => {
      this.value++;
      console.log(this.value);
    }, 10);
  }
};

counter.incLater(); // 1

Если заменить стрелочную функцию на обычную, this внутри колбэка будет определяться способом вызова колбэка, а не объектом counter.

const counter = {
  value: 0,
  incLater() {
    setTimeout(function () {
      "use strict";
      // this здесь undefined, так как это обычный вызов функции
      // console.log(this.value); // ошибка
    }, 10);
  }
};
Стрелочные функции плохо подходят в роли методов, если требуется динамический this при вызове obj.method().
const obj = {
  x: 1,
  getX: () => this && this.x
};

console.log(obj.getX()); // обычно undefined, так как this взят снаружи

Частые ситуации в JS

Обработчики событий DOM

В DOM-обработчиках, добавленных через addEventListener, обычная функция обычно получает this, равный элементу, на котором сработало событие. У стрелочной функции такого поведения нет, потому что this у неё лексический.

const button = document.querySelector("button");

button.addEventListener("click", function () {
  console.log(this === button); // true
});

button.addEventListener("click", () => {
  console.log(this === button); // false
});

Методы классов и потеря контекста

Методы классов — это обычные функции. При присваивании метода в переменную и вызове как fn() контекст теряется.

class User {
  constructor(name) {
    this.name = name;
  }
  getName() {
    return this.name;
  }
}

const u = new User("Светлана");
const fn = u.getName;

try {
  console.log(fn()); // strict: ошибка, так как this = undefined
} catch (e) {
  console.log("Ошибка из-за потерянного this");
}

Типовые способы избежать проблемы:

  • фиксировать контекст через bind (например, в конструкторе),
  • использовать стрелочную функцию там, где требуется лексический this (например, для колбэка), но не как универсальную замену методов.

Минимальная связь со спецификацией (без углубления)

В ECMAScript значение this при вызове обычной функции связано с тем, вызывается ли функция как «свойство объекта» (когда есть база вызова, например в obj.fn()), или как самостоятельная функция fn(). Для стрелочных функций this не формируется механизмом вызова, а берётся из внешнего лексического окружения.

На практике это сводится к правилу: необходимо смотреть на форму вызова, а не на место объявления функции.

В итоге: this в JavaScript (для обычных функций) определяется в момент вызова и зависит от формы вызова (obj.fn(), call/apply, bind, new). В строгом режиме при обычном вызове fn() значение this равно undefined. У стрелочных функций this берётся из внешней области и не меняется при call/apply/bind.