this в обычных и стрелочных функциях
В обычных функциях
thisопределяется в момент вызова (динамический контекст), в стрелочных — в момент создания функции (лексический контекст из окружающего кода).
Зачем нужно
this — одна из самых частых причин ошибок в JavaScript. Понимание разницы между обычными и стрелочными функциями позволяет правильно использовать контекст в методах классов, обработчиках событий, таймерах и callback-функциях.
Где используется
- Методы объектов и классов — обычные функции с динамическим
this - Обработчики событий —
thisуказывает на элемент (обычная функция) - Таймеры и промисы — стрелочные функции для сохранения контекста
- Методы массивов (
map,forEach) — стрелочные, чтобы не потерятьthis
this в обычных функциях
// Зависит от того, КАК вызвана функция:
// 1. Вызов как метод объекта
const obj = {
name: 'Объект',
greet {
return `Привет от ${this.name}`; // this = obj
}
};
obj.greet; // "Привет от Объект"
// 2. Вызов как обычная функция
function show() {
return this; // в strict mode — undefined; иначе window/global
}
show;
// 3. Вызов через new
function Person(name) {
this.name = name; // this = новый объект
}
const p = new Person('Иван');
// 4. Явная привязка через call/apply/bind
function greet() { return this.name; }
greet.call({ name: 'Мария' }); // "Мария"
const bound = greet.bind({ name: 'Пётр' });
bound; // "Пётр"
this в стрелочных функциях
// Стрелочная функция НЕ имеет своего this
// Она берёт this из лексического окружения (где объявлена)
class Timer {
constructor {
this.seconds = 0;
}
start {
// Стрелочная: this = экземпляр Timer
setInterval(() => {
this.seconds++; // работает корректно
}, 1000);
}
}
// Обычная функция в том же контексте:
class TimerBroken {
start {
setInterval(function {
this.seconds++; // this = undefined (strict) или window!
}, 1000);
}
}
Потеря контекста и способы исправления
class Button {
constructor(label) {
this.label = label;
}
// Проблема: потеря this при передаче как callback
handleClick {
console.log(`Нажата: ${this.label}`);
}
}
const btn = new Button('Сохранить');
// Плохо: this потеряется
document.addEventListener('click', btn.handleClick);
// Решение 1: bind
document.addEventListener('click', btn.handleClick.bind(btn));
// Решение 2: стрелочная обёртка
document.addEventListener('click', () => btn.handleClick);
// Решение 3: стрелочное поле класса (class field)
class ButtonFixed {
constructor(label) {
this.label = label;
}
handleClick = () => { // стрелочная функция как поле — всегда верный this
console.log(`Нажата: ${this.label}`);
};
}
Таблица правил
| Как вызвана | this |
|---|---|
Метод объекта obj.fn |
obj |
Обычный вызов fn |
undefined (strict) / window |
new Fn |
Новый объект |
fn.call(ctx) |
ctx |
| Стрелочная функция | this из окружения |
| Обработчик события (обычная) | Элемент DOM |
Частые ошибки
- Деструктурирование метода —
const { greet } = obj; greet;—thisтеряется; используйтеbindили стрелочные поля. - Стрелочная функция как метод объекта —
const obj = { name: 'A', greet: => this.name }—thisбудет из внешнего контекста, не объекта. - Использование
bindвrender/рендер-методах — создаёт новую функцию на каждый рендер; выносите в конструктор или используйте class fields.
Связанные темы
Ресурсы
⚡ Источник: Как работает this в javascript · AsForJS
- 📅 2023-05-07 · YouTube · ID:
4tg4qokVS9o - Тезисы:
- Нормальная функция (любая, кроме стрелочной) имеет скрытый параметр
this - Стрелочная функция этот параметр не имеет — при доступе к
thisпоиск идёт по цепочке окружений до родительского нормального - Главный вопрос для отладки: в какой нотации вызвана функция? Dot —
this= объект перед точкой; иначе по правилам call/apply/bind, new, или undefined - Возврат стрелки из метода навсегда фиксирует
thisмомента определения
- Нормальная функция (любая, кроме стрелочной) имеет скрытый параметр
- Цитата: «Никаких контекстов, ничего придумывать не надо. Всё просто и примитивно... Он максимально логичен и максимально прост.»
🎓 Источник: Функции, стрелочные функции, контексты
- 📅 2018-09-27 · YouTube · ID:
pn5myCmpV2U - Тезисы:
- У
functionесть функциональный (аргументы, локальные) и объектный (this) контекст - У лямбды объектного контекста нет —
thisберётся из контекста выше bindработает только дляfunction, лямбды не привязываются
- У