Метод-вызов — ситуация, когда функция запускается через объект-владелец: user.greet(), arr.push(1), element.appendChild(child). Слева от точки стоит объект — и этот же объект становится 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 fn = user.greet) или передача метода как callback (setInterval(t.tick, 1000)) отделяют функцию от её объекта — и this внутри теряется. Это самая частая ошибка с методами в JavaScript, и она заслуживает отдельного разбора: подглава 3.2 «Опасность: отделение метода от его объекта». Там — типичные сценарии, три способа обойти проблему и разбор React-паттернов до появления hooks.
Практические 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.
Если ты пишешь класс или объект с методами — вызов метода объекта «слева от точки» — самое надёжное поведение. Проблемы начинаются, когда метод куда-то передаётся отдельно от объекта — это и разбирается в следующей подглаве.