$ sudo teach IT
РАЗДЕЛ 8 · УРОК 1

Объекты глубже: методы и this

Вы уже знаете, что объект — это набор свойств. Но объекты умеют гораздо больше: они могут содержать функции — и тогда эти функции называют методами. Именно методы делают объекты «живыми» и дают им поведение. А чтобы метод знал, с какими данными работать, существует ключевое слово this.

⏱ ~25 минут 🎓 Для новичков ⚡ JavaScript

🔧 Метод — это функция внутри объекта

Давайте начнём с самого простого. Вы уже знаете, как создать объект с обычными свойствами — числами, строками, булевыми значениями:

const user = {
  name: "Алексей",
  age: 28,
  isPremium: true
};

Это статические данные — они хранят информацию, но ничего не делают. А теперь представьте, что мы хотим, чтобы объект умел приветствовать. Звучит немного по-мультяшному, но идея абсолютно практичная. Для этого в объект можно положить функцию:

const user = {
  name: "Алексей",
  age: 28,
  greet: function() {
    console.log("Привет! Меня зовут Алексей.");
  }
};

user.greet(); // Привет! Меня зовут Алексей.

Вот и всё! Функция внутри объекта называется методом. Синтаксис вызова — через точку: объект.метод(). Вы уже делали это тысячу раз — console.log(), arr.push(), str.toUpperCase() — это всё методы!

💡 Запомните: если значение свойства — это функция, свойство называется методом. Метод — это функция, принадлежащая объекту.

В современном JavaScript (ES6+) есть более краткая запись методов — без слова function:

const user = {
  name: "Алексей",
  age: 28,
  greet() {                       // краткий синтаксис — рекомендуется
    console.log("Привет, мир!");
  },
  sayAge() {
    console.log("Мне 28 лет.");
  }
};

user.greet();   // Привет, мир!
user.sayAge();  // Мне 28 лет.

Оба варианта работают одинаково — краткая запись просто чище и читабельнее. Используйте её в новом коде.

🔍 Ключевое слово this — «я сам»

Вернёмся к методу greet. Там мы жёстко вписали строку "Привет! Меня зовут Алексей.". Но что если у нас будет сто пользователей? Нам что, для каждого писать отдельную функцию? Конечно нет.

Метод должен уметь обращаться к свойствам своего объекта — к имени, возрасту, другим данным. Для этого существует this.

🎯 this — это ссылка на объект, в контексте которого был вызван метод. Проще говоря: когда вы пишете this внутри метода, вы имеете в виду этот самый объект.

Посмотрим на примере:

const user = {
  name: "Алексей",
  age: 28,
  greet() {
    console.log("Привет! Меня зовут " + this.name + ".");
    // this.name — обращение к свойству name этого объекта
  },
  introduce() {
    console.log(`Я ${this.name}, мне ${this.age} лет.`);
  }
};

user.greet();      // Привет! Меня зовут Алексей.
user.introduce();  // Я Алексей, мне 28 лет.

Когда JavaScript выполняет user.greet(), он автоматически устанавливает this равным user. Поэтому внутри метода this.name — это то же самое, что user.name.

Почему нельзя просто написать user.name вместо this.name прямо внутри метода? Технически можно — но это плохая практика. Что если объект переименуют? Что если этот метод скопируют в другой объект? С this код работает универсально, он всегда ссылается на «текущий объект».

❌ Плохо (хардкод имени)
greet() {
  console.log(
    "Привет, " + user.name
  );
}
✅ Хорошо (через this)
greet() {
  console.log(
    "Привет, " + this.name
  );
}

🏦 Практика: объект «Банковский счёт»

Теперь создадим реальный, полезный пример — объект банковского счёта. У него будут свойства (владелец, баланс) и методы (пополнить, снять, посмотреть баланс). Это отличная иллюстрация того, как данные и поведение живут вместе в одном объекте.

const account = {
  owner: "Мария Петрова",
  balance: 15000,

  deposit(amount) {
    if (amount <= 0) {
      console.log("Сумма пополнения должна быть больше нуля.");
      return;
    }
    this.balance += amount;
    console.log(`Пополнено на ${amount} ₽. Баланс: ${this.balance} ₽`);
  },

  withdraw(amount) {
    if (amount <= 0) {
      console.log("Сумма снятия должна быть больше нуля.");
      return;
    }
    if (amount > this.balance) {
      console.log("Недостаточно средств!");
      return;
    }
    this.balance -= amount;
    console.log(`Снято ${amount} ₽. Баланс: ${this.balance} ₽`);
  },

  getBalance() {
    console.log(`${this.owner}, ваш баланс: ${this.balance} ₽`);
    return this.balance;
  }
};

account.getBalance();   // Мария Петрова, ваш баланс: 15000 ₽
account.deposit(5000);  // Пополнено на 5000 ₽. Баланс: 20000 ₽
account.withdraw(3000); // Снято 3000 ₽. Баланс: 17000 ₽
account.withdraw(50000); // Недостаточно средств!
account.getBalance();   // Мария Петрова, ваш баланс: 17000 ₽

Разберём детально, что происходит в методе deposit:

  • deposit(amount) — метод принимает аргумент, сколько пополнить
  • Проверяем, что сумма положительная — защита от ошибок
  • this.balance += amount — изменяем баланс этого объекта
  • Выводим сообщение с текущим this.balance — данные уже обновлены

Обратите внимание: методы не просто читают this — они могут его изменять. this.balance += amount реально меняет свойство объекта. Это и есть «поведение» объекта — он реагирует на внешние действия и сам меняет своё состояние.

⚠️ Важно: this — это не копия объекта, а именно ссылка на него. Поэтому изменения через this.balance = ... реально меняют исходный объект.

⚠️ Почему стрелочные функции не подходят для методов

Вы знаете стрелочные функции: () => {}. Они удобны и компактны. Но для методов объекта их нельзя использовать. Давайте разберёмся почему — это одна из самых частых ловушек для новичков.

Посмотрим на проблему в коде:

const user = {
  name: "Игорь",
  greet: () => {
    // Стрелочная функция — ОШИБКА!
    console.log("Привет, " + this.name);
  }
};

user.greet(); // Привет, undefined

Почему undefined? Всё дело в том, как стрелочные функции работают с this.

Обычная функция (function)

Имеет своё значение this. При вызове как метода obj.method(), this автоматически становится равным obj. Именно это нам нужно.

Стрелочная функция (() => {})

Не имеет своего this. Она берёт this из окружающего контекста (обычно это глобальный объект или undefined в строгом режиме). Для метода это катастрофа.

Это сознательное дизайнерское решение — стрелочные функции были созданы именно для случаев, когда нужно сохранить внешний this, а не создать новый. Они отлично подходят для колбэков внутри методов:

const timer = {
  seconds: 0,
  start() {
    // Здесь this = timer ✓
    setInterval(() => {
      // Стрелочная функция берёт this из start()
      // то есть this по-прежнему = timer ✓
      this.seconds++;
      console.log(`Прошло ${this.seconds} сек.`);
    }, 1000);
  }
};

timer.start();

Если бы внутри setInterval была обычная функция, this там потерялся бы. Стрелочная функция — идеальный колбэк именно потому, что «прозрачна» для this.

✅ Правило: для методов объекта — только обычные функции (function или краткий синтаксис method() {}). Стрелочные функции — для колбэков внутри методов.

➕ Динамическое добавление методов

Объекты в JavaScript гибкие — методы можно добавлять уже после создания объекта. Это называется динамическим добавлением:

const car = {
  brand: "Toyota",
  speed: 0
};

// Объект создан, теперь добавим метод
car.accelerate = function(kmh) {
  this.speed += kmh;
  console.log(`${this.brand} разгоняется. Скорость: ${this.speed} км/ч`);
};

car.brake = function() {
  this.speed = 0;
  console.log(`${this.brand} остановился.`);
};

car.accelerate(60);  // Toyota разгоняется. Скорость: 60 км/ч
car.accelerate(40);  // Toyota разгоняется. Скорость: 100 км/ч
car.brake();         // Toyota остановился.

Синтаксис простой: объект.новыйМетод = function() { ... }. После этой строки у объекта появляется новый метод.

Это удобно, но не злоупотребляйте: если структура объекта известна заранее, лучше определить все методы при создании. Динамическое добавление полезно в особых ситуациях — когда поведение нужно добавить условно или «по требованию».

🔑 Кстати, удалить метод из объекта можно оператором delete: delete car.brake — и метода больше нет.

🌐 this зависит от того, КАК вызвана функция

Самое коварное в this — его значение зависит не от того, где функция написана, а от того, как она вызвана. Рассмотрим интересный пример:

function greet() {
  console.log("Привет, " + this.name);
}

const userA = { name: "Анна", greet: greet };
const userB = { name: "Борис", greet: greet };

userA.greet(); // Привет, Анна   (this = userA)
userB.greet(); // Привет, Борис  (this = userB)

Одна и та же функция greet — но при вызове через разные объекты она видит разный this! Когда мы пишем userA.greet(), JavaScript перед вызовом функции смотрит: «что стоит слева от точки?» Это userA — значит, this = userA.

⚠️ Ловушка потери контекста: если вы «оторвёте» метод от объекта и вызовете его как обычную функцию, this потеряется:

const fn = userA.greet;
fn(); // Привет, undefined  — this потерян!

Это продвинутая тема — как это фиксить через bind, call, apply — будет в следующих уроках. Сейчас главное запомнить: вызывайте методы только через объект, и this всегда будет правильным.

✅ Итоги урока

  • Метод — это функция, которая является свойством объекта. Вызывается через точку: obj.method()
  • Краткий синтаксис методов: methodName() {} — предпочтительный вариант
  • this внутри метода — ссылка на объект, через который вызван метод
  • Через this методы читают и изменяют свойства своего объекта
  • Стрелочные функции не имеют своего this — не используй их как методы объекта
  • Методы можно добавлять к объекту динамически: obj.newMethod = function() {}
  • this определяется в момент вызова: что стоит слева от точки — тот и есть this

В следующем уроке разберём геттеры и сеттеры — специальные методы, которые выглядят как обычные свойства, но работают как функции. 🚀

Методы и this

7 вопросов

Объект «Калькулятор» с методами

Создайте объект calculator с методами add(a, b), subtract(a, b), multiply(a, b), divide(a, b). Каждый метод возвращает результат соответствующей операции. Метод divide должен возвращать строку 'Ошибка: деление на ноль' при попытке деления на 0.

Объект «Книга» с методами и this

Создайте объект book со свойствами title, author, year и методом getInfo, который возвращает строку '«title» — author (year)'. Используйте this внутри метода для доступа к свойствам объекта.