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() | Новый созданный объект |
| Стрелочная функция | () => this | this берётся из внешнего лексического окружения и не меняется call/apply/bind |
Схема принятия решения
Упрощённый порядок проверки для вычисления this:
- Если функция стрелочная, то
thisберётся снаружи (лексически) - Иначе, если вызов с
new, тоthis— новый объект - Иначе, если есть
bind, тоthis— привязанный объект - Иначе, если вызов через
call/apply, тоthis— переданный объект - Иначе, если вызов как
obj.method(), тоthis—obj(то, что слева от точки) - Иначе обычный вызов:
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.