Вызов метода объекта — самый частый способ «позвать» функцию в реальном коде. Правило одной строчкой:
this — это объект, которому принадлежит метод при его вызове.
Проще: то, что стоит слева от точки в момент вызова. user.greet() → this внутри greet будет user. arr.push(1) → this внутри push будет arr. И так далее.
Базовый пример
const calc = {
num: 0,
increment() {
console.log(this === calc); // => true
this.num += 1;
return this.num;
}
};
calc.increment(); // => 1
calc.increment(); // => 2
Вызов calc.increment() устанавливает контекст функции increment в calc. Внутри метода this.num означает calc.num. Всё естественно: обращаешься к своим же полям через this.
Обрати внимание на укороченный синтаксис метода — increment() { ... } вместо increment: function() { ... }. Оба работают одинаково; в современном коде используется short-версия.
this работает и с наследованием через прототип
Если метод определён на прототипе объекта, при вызове он всё равно получает контекст самого объекта, а не прототипа:
const dogPrototype = {
getName() {
console.log(this === myDog); // => true, не dogPrototype
return this.name;
}
};
const myDog = Object.create(dogPrototype);
myDog.name = 'Бобик';
myDog.getName(); // => 'Бобик'
Object.create(dogPrototype) создаёт myDog, у которого прототипом является dogPrototype. Метод getName формально живёт на прототипе, но при вызове myDog.getName() его контекст — сам myDog. Это логично: слева от точки стоит myDog, значит this — myDog.
Классы ES6 работают так же
Синтаксис классов, появившийся в ES2015, — это синтаксический сахар над прототипным наследованием. Правило про this в методе — ровно такое же:
class Planet {
constructor(name) {
this.name = name;
}
getName() {
console.log(this === earth); // => true
return this.name;
}
}
const earth = new Planet('Земля');
earth.getName(); // => 'Земля'
Тело класса автоматически работает в строгом режиме, но правило про this в методе не меняется — контекст всё равно экземпляр (earth).
Ловушка: отделение метода от объекта
Самая частая ошибка с this в методах — отделить метод от его объекта и вызвать его как обычную функцию. Разбор в следующей подглаве, но кратко:
const user = {
name: 'Аня',
greet() {
return `Привет, ${this.name}`;
}
};
user.greet(); // => 'Привет, Аня'
// а теперь отделили метод:
const greetFn = user.greet;
greetFn(); // => 'Привет, undefined' (или TypeError в strict)
Когда мы делаем const greetFn = user.greet, мы копируем ссылку на функцию, но не «прикрепление» к объекту. При последующем вызове greetFn() — это уже простой вызов, у него this = window или undefined.
Именно эта ловушка обескураживает разработчиков, передающих метод как callback:
class Timer {
constructor() { this.seconds = 0; }
tick() {
this.seconds += 1;
console.log(this.seconds);
}
}
const t = new Timer();
// НЕ РАБОТАЕТ — this внутри tick() потерян
setInterval(t.tick, 1000);
// РАБОТАЕТ — сохраняем контекст через bind
setInterval(t.tick.bind(t), 1000);
// РАБОТАЕТ — оборачиваем в стрелочную функцию
setInterval(() => t.tick(), 1000);
setInterval(t.tick, ...) передаёт саму функцию, отделяя её от t. Когда таймер её вызовет, this уже не будет ссылаться на t. Решение — либо bind, либо стрелочная-обёртка. Оба работают, стрелочная — современный дефолт.
Практические use cases
Метод-вызов — неотъемлимая часть объектно-ориентированного JavaScript. Пара идиом, где это встречается регулярно:
- Chainable API. Методы возвращают this, чтобы можно было вызывать цепочкой: query.filter(x).sort().limit(10). Каждый вызов сохраняет контекст.
- Fluent builders. builder.set('key', 'value').set('other', 42).build() — тоже опирается на возврат this.
- Event emitters. emitter.on(event, handler) внутри устанавливает this.handlers[event]. Всё через this.
- DOM API. element.appendChild(child) — this внутри метода это element. Отделять appendChild от элемента бессмысленно, но пример показывает, как метод-вызов работает и в DOM.
Если ты пишешь класс или объект с методами — вызов метода объекта «слева от точки» — самое надёжное поведение. Проблемы начинаются, когда метод куда-то передаётся отдельно от объекта — это и разбирается в следующей подглаве.