this
this— ключевое слово, ссылающееся на объект, в контексте которого выполняется функция. Значениеthisопределяется в момент вызова функции, а не при её создании (кроме стрелочных функций).
Зачем нужно
Понимание this — обязательное условие для работы с объектами, классами, DOM-обработчиками и фреймворками. Неправильное понимание this — источник большинства багов у начинающих разработчиков.
Где используется
Методы объектов, классы, конструкторы, обработчики событий, callback-функции, React-компоненты, jQuery, Node.js.
Предпосылки
Function Declaration, Arrow Function, Замыкания (Closures)
Четыре правила привязки this
1. Default binding — вызов без контекста
function showThis() {
console.log(this);
}
showThis;
// В strict mode: undefined
// Без strict mode: window (globalThis)
'use strict';
function strictFn() {
console.log(this); // undefined
}
strictFn;
2. Implicit binding — вызов как метод объекта
const user = {
name: 'Иван',
greet {
console.log(`Привет, ${this.name}`);
}
};
user.greet; // "Привет, Иван" — this === user
3. Explicit binding — call, apply, bind
function greet() {
console.log(`Привет, ${this.name}`);
}
const user = { name: 'Мария' };
greet.call(user); // "Привет, Мария"
greet.apply(user); // "Привет, Мария"
const bound = greet.bind(user);
bound; // "Привет, Мария"
4. New binding — вызов с new
function User(name) {
// this = {} (новый пустой объект)
this.name = name;
this.greet = function {
console.log(`Привет, ${this.name}`);
};
// return this; (неявно)
}
const ivan = new User('Иван');
ivan.greet; // "Привет, Иван" — this === ivan
Приоритет привязки
От наивысшего к низшему:
1. new binding — new Fn
2. Explicit binding — call/apply/bind
3. Implicit binding — obj.method
4. Default binding — fn
function greet() {
console.log(this.name);
}
const obj1 = { name: 'Один', greet };
const obj2 = { name: 'Два' };
// Implicit
obj1.greet; // "Один"
// Explicit > Implicit
obj1.greet.call(obj2); // "Два"
// new > Explicit
const BoundGreet = greet.bind(obj2);
const instance = new BoundGreet();
// this — новый объект, НЕ obj2
this в разных контекстах
Глобальный контекст
console.log(this); // В браузере: window, в Node.js модуле: module.exports
// В ES-модуле
console.log(this); // undefined
Метод объекта
const calculator = {
value: 0,
add(n) {
this.value += n;
return this; // для цепочки вызовов
},
subtract(n) {
this.value -= n;
return this;
},
getResult {
return this.value;
}
};
const result = calculator.add(10).add(5).subtract(3).getResult;
console.log(result); // 12
Конструктор и класс
class Animal {
constructor(name) {
this.name = name; // this — создаваемый экземпляр
}
speak {
console.log(`${this.name} говорит`);
}
}
class Dog extends Animal {
speak {
console.log(`${this.name} лает`); // this — экземпляр Dog
}
}
const dog = new Dog('Бобик');
dog.speak; // "Бобик лает"
Обработчик события
const button = document.querySelector('#myBtn');
button.addEventListener('click', function(event) {
console.log(this); // <button> элемент
console.log(event.target); // тот же <button>
this.classList.toggle('active');
});
Стрелочная функция — лексический this
const team = {
name: 'Альфа',
members: ['Иван', 'Мария'],
showMembers {
// Стрелочная функция берёт this из showMembers
this.members.forEach(member => {
console.log(`${this.name}: ${member}`);
});
}
};
team.showMembers;
// "Альфа: Иван"
// "Альфа: Мария"
Потеря контекста
Передача метода как callback
const user = {
name: 'Иван',
greet {
console.log(`Привет, ${this.name}`);
}
};
// this потерян!
const fn = user.greet;
fn; // "Привет, undefined"
// setTimeout тоже теряет this
setTimeout(user.greet, 100); // "Привет, undefined"
// Решение 1: bind
setTimeout(user.greet.bind(user), 100);
// Решение 2: обёртка стрелочной функцией
setTimeout( => user.greet, 100);
Деструктуризация метода
const { greet } = user;
greet; // this потерян!
// Решение: не деструктуризируйте методы, или bind
Вложенные функции
const obj = {
value: 42,
method {
// Вложенная функция — свой this
function inner() {
console.log(this.value); // undefined (strict) или window.value
}
inner;
// Решение 1: стрелочная функция
const innerArrow = () => {
console.log(this.value); // 42
};
innerArrow;
// Решение 2: сохранить this
const self = this;
function innerSelf() {
console.log(self.value); // 42
}
innerSelf;
}
};
this в классах
class Button {
constructor(label) {
this.label = label;
this.clicks = 0;
// Привязка в конструкторе
this.handleClick = this.handleClick.bind(this);
}
handleClick {
this.clicks++;
console.log(`${this.label}: ${this.clicks} кликов`);
}
// Альтернатива: class field + arrow
handleClickArrow = () => {
this.clicks++;
console.log(`${this.label}: ${this.clicks} кликов`);
};
}
const btn = new Button('Кнопка');
document.querySelector('#btn').addEventListener('click', btn.handleClick);
globalThis
// Универсальный глобальный объект (ES2020)
console.log(globalThis);
// В браузере: window
// В Node.js: global
// В Web Worker: self
Частые ошибки
1. this в setTimeout
class Timer {
constructor {
this.seconds = 0;
}
start {
// Плохо
// setInterval(function { this.seconds++; }, 1000);
// Хорошо
setInterval(() => { this.seconds++; }, 1000);
}
}
2. this в обработчике событий React
class App extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
// Без bind: this будет undefined в handleClick
this.handleClick = this.handleClick.bind(this);
}
handleClick {
this.setState({ count: this.state.count + 1 });
}
}
3. Ожидание лексического this от обычной функции
const obj = {
items: [1, 2, 3],
process {
// forEach с обычной функцией — this потерян
this.items.forEach(function(item) {
// console.log(this.items); // undefined!
});
// Решение: стрелка
this.items.forEach((item) => {
console.log(this.items); // [1, 2, 3]
});
}
};
Практика
- Создай объект с методом и вызови его напрямую и через переменную — сравни this
- Напиши класс с методом, который теряет this в setTimeout — исправь тремя способами
- Используй call/apply для вызова функции с разными контекстами
- Создай цепочку вызовов (method chaining) через return this
- Определи, что выведет this в 5 разных контекстах (глобальный, метод, конструктор, стрелка, событие)
Связанные темы
Ресурсы
🎓 Источник: Функции, стрелочные функции, контексты, замыкания
- 📅 2018-09-27 · YouTube · ID:
pn5myCmpV2U - Тезисы:
- У функции два контекста: функциональный (аргументы, локальные) и объектный (через
this) - Не у всех функций есть объектный контекст
- У lambda (arrow) нет своего
this— она берёт его из контекста выше call— аргументы через запятую,apply— массив, первый аргумент —this
- У функции два контекста: функциональный (аргументы, локальные) и объектный (через
- Цитата: «У функции есть один контекст. Он называется функциональный контекст. Но у нее есть еще объектный контекст. Но не у всех функций есть объектный контекст.»
⚡ Источник: Как работает this в javascript · AsForJS
- 📅 2023-05-07 · YouTube · ID:
4tg4qokVS9o - Тезисы:
- Главный тезис:
thisв JS НЕ контекст, никогда им не был this— особый идентификатор, разрешаемый формой вызова- Ровно 3 способа задать
this: call/apply/bind, new, dot notation - По умолчанию
this = undefinedв strict mode - У любой нормальной функции
this— скрытый нулевой параметр - Стрелочная функция не задаёт
this, ищет его как обычный идентификатор по цепочке окружений - В non-strict примитив
thisпроходитToObject(12 → Number{12}) - API хост-среды (HTML5 addEventListener, Node setTimeout) вправе нарушать правила JS
bindперекрывает даже поведение API
- Главный тезис:
- Цитата: «this в JavaScript — это не контекст, никогда им не было, никогда не будет, ничего общего с контекстом.»
- Цитата (мнемоника): «Хочешь знать, чему this сравняется, проверь, как твоя функция запускается.»
- Подробнее: this, this, this в callback и API