this в JavaScript: четыре правила связывания
Содержание
О проверке кода. Примеры не запускались — в отличие от материалов по Node.js с фактическим выводом. Говорим прямо, а не ставим бейдж «проверено».
this — самая скользкая тема в JavaScript. Причина путаницы в одном: this не про то, где функция написана, а про то, как её вызвали. Разберём по правилам.
Главный принцип
Значение this определяется в момент вызова, а не в момент объявления. Одна и та же функция при разных вызовах получит разный this. Поэтому смотреть надо не на код функции, а на строку, где её зовут.
Есть ровно четыре правила, и они проверяются по порядку.
Правило 1: вызов как метод
const user = {
name: 'Аня',
greet() {
return `Привет, ${this.name}`; // this — это user
},
};
user.greet(); // "Привет, Аня"
Функцию вызвали через точку (user.greet()) — this равен объекту слева от точки. Это самый интуитивный случай: метод знает, к какому объекту он привязан.
Правило 2: вызов сам по себе
function show() {
return this;
}
show(); // undefined в строгом режиме (глобальный объект в нестрогом)
Функцию вызвали просто по имени, без объекта — this равен undefined (в строгом режиме и в модулях). В нестрогом режиме это глобальный объект, но полагаться на это не стоит.
Именно отсюда растёт «потеря контекста»:
const user = {
name: 'Аня',
greet() { return `Привет, ${this.name}`; },
};
const fn = user.greet; // оторвали метод от объекта
fn(); // ❌ TypeError: this.name — this это undefined!
Метод оторвали от объекта и вызвали сам по себе — связь потеряна. То же происходит, когда метод передают колбэком.
Правило 3: вызов через new
function User(name) {
this.name = name; // this — новый пустой объект
}
const u = new User('Борис');
u.name; // "Борис"
Вызов через new создаёт новый объект и делает его this. На этом построены конструкторы и классы — там this внутри constructor это создаваемый экземпляр.
Правило 4: явная привязка
function greet() { return `Привет, ${this.name}`; }
const user = { name: 'Вера' };
greet.call(user); // "Привет, Вера" — вызвать с this = user
greet.apply(user); // то же, аргументы массивом
const bound = greet.bind(user); // НЕ вызывает, возвращает функцию с this = user
bound(); // "Привет, Вера"
call, apply, bind задают this вручную:
call— вызвать сейчас,thisпервым аргументом, остальные через запятую;apply— то же, но аргументы массивом;bind— не вызывать, а вернуть новую функцию с намертво привязаннымthis.
bind — классическое решение потери контекста: привязали метод к объекту раз и передавайте куда угодно.
-
1
Вызов через
new?→this= новый создаваемый объект -
2
Через
call/apply/bind?→this= то, что передали явно -
3
Через точку
obj.method()?→this= объект слева от точки -
4
Просто
fn()?→this= undefined (строгий режим)
Стрелочные функции: своего this нет
Пятый случай, который на самом деле отменяет остальные четыре. У стрелочной функции нет собственного this — она берёт его из окружающего кода, где объявлена, и никакой вызов это не меняет.
const timer = {
seconds: 0,
start() {
// ❌ обычная функция: свой this, здесь undefined
setInterval(function () {
this.seconds++; // this — НЕ timer!
}, 1000);
// ✅ стрелка: this взят из start(), это timer
setInterval(() => {
this.seconds++;
}, 1000);
},
};
В колбэке обычная функция вызывается «сама по себе» (правило 2), и this теряется. Стрелка же не имеет своего this — она использует this метода start, то есть timer. До появления стрелок это лечили строкой const self = this — её до сих пор видно в старом коде.
Обратная сторона: стрелка не годится как метод объекта. this внутри неё будет не объектом, а тем, что снаружи:
const user = {
name: 'Аня',
greet: () => `Привет, ${this.name}`, // ❌ this — не user!
};
Практическое правило
Свести всё к простому выбору:
- метод объекта — обычная функция (
method() {}),thisбудет объектом; - колбэк внутри метода — стрелка, чтобы сохранить
this; - передаёте метод куда-то —
bind, чтобыthisне потерялся; - сомневаетесь — посмотрите на строку вызова, не на объявление.
В современном коде с классами и хуками эта боль почти ушла: методы вызываются как методы, колбэки пишутся стрелками. Но понимать, откуда берётся this, всё равно нужно — иначе очередной undefined в колбэке останется загадкой.
Смежные темы: функции — обычные против стрелочных; классы — где this это экземпляр; прототипы. Полный список — в уроках JavaScript.