Метод-вызов — ситуация, когда функция запускается через объект-владелец: 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, значит thismyDog.

Классы 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.

Если ты пишешь класс или объект с методами — вызов метода объекта «слева от точки» — самое надёжное поведение. Проблемы начинаются, когда метод куда-то передаётся отдельно от объекта — это и разбирается в следующей подглаве.