Вызов метода объекта — самый частый способ «позвать» функцию в реальном коде. Правило одной строчкой:

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 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.

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