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 встречается на практике чаще всего.