Методы классов JavaScript: полное руководство

Раздел: Программирование на JavaScript -> Объектно-ориентированное программирование

Методы классов в JavaScript

Основной способ определения методов в современном JavaScript - использование синтаксиса class (ES6). Метод объявляется внутри класса без ключевого слова function и автоматически помещается в прототип класса, становясь общим для всех экземпляров.

class User {
  constructor(name) {
    this.name = name;
  }
  sayHi() {
    return `Привет, ${this.name}!`;
  }
}
const user = new User('Анна');
console.log(user.sayHi()); // "Привет, Анна!"
console.log(user.hasOwnProperty('sayHi')); // false (метод в прототипе)

Javascript object (объекты в javascript)

Привет, Анна!

Public js (модификатор public в javascript)

Типичные ошибки: забытая запятая между методами (в class запятые не нужны), попытка использовать function внутри класса. Решение: использовать короткий синтаксис без function и без запятых.

Проблема с контекстом: при передаче метода как колбэка this может потеряться. Решение: использовать стрелочную функцию в качестве метода (см. вариант с контекстом) или привязать контекст через bind.

Как добавить метод в класс до появления синтаксиса class?

До ES6 методы добавлялись через прототип с помощью prototype. Этот подход остаётся рабочим и позволяет динамически изменять класс после его объявления.

function User(name) {
  this.name = name;
}
User.prototype.sayHi = function() {
  return `Привет, ${this.name}!`;
};
const user = new User('Иван');
console.log(user.sayHi()); // "Привет, Иван!"

Javascript class method (методы классов в javascript)

Привет, Иван!

Ошибка: забыть ключевое слово new или присвоить метод в конструкторе (каждому экземпляру) - приводит к дублированию кода. Решение: всегда добавлять методы в прототип, а не в конструктор.

Цель: совместимость со старыми стандартами или необходимость динамического расширения прототипа.

Как создать метод, связанный с классом, а не с экземпляром?

Статические методы (static) вызываются на самом классе, а не на экземпляре. Они часто используются для фабричных функций или утилит.

class MathHelper {
  static sum(a, b) {
    return a + b;
  }
  static multiply(a, b) {
    return a * b;
  }
}
console.log(MathHelper.sum(2, 3)); // 5
console.log(MathHelper.multiply(2, 3)); // 6

Внутри статического метода this ссылается на сам класс, а не на экземпляр.

Ошибка: попытка вызвать статический метод через экземпляр - приведёт к ошибке TypeError. Решение: вызывать статический метод только через имя класса.

Как скрыть метод от внешнего доступа?

Приватные методы помечаются символом # и доступны только внутри класса. Это гарантирует инкапсуляцию.

class User {
  #password;
  constructor(name, password) {
    this.name = name;
    this.#password = password;
  }
  #validatePassword() {
    return this.#password.length >= 8;
  }
  checkPasswordStrength() {
    return this.#validatePassword() ? 'Сильный' : 'Слабый';
  }
}
const user = new User('Мария', '12345678');
console.log(user.checkPasswordStrength()); // "Сильный"
// console.log(user.#validatePassword()); // SyntaxError

Особенность: приватные методы не поддерживаются старыми браузерами. Решение: использовать замыкание в конструкторе для имитации приватности (функция внутри constructor).

class User {
  constructor(name, password) {
    this.name = name;
    const validatePassword = () => password.length >= 8;
    this.checkPasswordStrength = () => validatePassword() ? 'Сильный' : 'Слабый';
  }
}

Цель: инкапсуляция внутренней логики, защита от случайного использования.

Как выполнить код при чтении или записи свойства?

Геттеры (get) и сеттеры (set) позволяют перехватывать доступ к свойству и выполнять произвольные действия. Они выглядят как обычные свойства, но являются методами.

class User {
  constructor(first, last) {
    this.firstName = first;
    this.lastName = last;
  }
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
  set fullName(value) {
    [this.firstName, this.lastName] = value.split(' ');
  }
}
const user = new User('Петр', 'Иванов');
console.log(user.fullName); // "Петр Иванов"
user.fullName = 'Сергей Смирнов';
console.log(user.firstName); // "Сергей"

Ошибка: сеттер и геттер должны быть объявлены с одним именем. Если сеттер отсутствует, присваивание будет проигнорировано в строгом режиме или вызовет ошибку. Решение: всегда объявлять оба, если требуется запись.

Цель: валидация, вычисляемые свойства, реализация доступа с побочными эффектами.

Как расширить или изменить поведение метода родительского класса?

Наследование через extends и вызов родительского метода через super. Дочерний класс может переопределить метод, сохранив часть функциональности.

class Animal {
  speak() {
    return 'Звук';
  }
}
class Dog extends Animal {
  speak() {
    return `${super.speak()} - Гав!`;
  }
}
const dog = new Dog();
console.log(dog.speak()); // "Звук - Гав!"

Ошибка: забыть вызвать super() в конструкторе наследника - приведёт к ошибке ReferenceError. Решение: всегда вызывать super() до обращения к this.

Цель: полиморфизм, переиспользование кода, расширение функциональности.

Как создать метод с зафиксированным контекстом this?

Стрелочные функции в качестве метода класса (свойство-стрелка) не имеют собственного this и захватывают его из внешней области (класса). Это удобно для колбэков.

class Counter {
  count = 0;
  increment = () => {
    this.count++;
  };
}
const counter = new Counter();
setTimeout(counter.increment, 100); // this не теряется
setTimeout(() => console.log(counter.count), 200); // 1

Недостаток: такие методы не помещаются в прототип, а создаются для каждого экземпляра, увеличивая потребление памяти. Решение: использовать bind в constructor, если важна производительность.

Цель: работа с событиями, таймерами, передача метода без потери контекста.

Расширенные примеры методов классов

Ниже приведены дополнительные сценарии использования методов классов в JavaScript, демонстрирующие нестандартные приёмы.

Цепочка вызовов методов (method chaining)

Для реализации цепочек метод должен возвращать this.

Пример
class Calculator {
  constructor(value = 0) {
    this.value = value;
  }
  add(n) {
    this.value += n;
    return this;
  }
  multiply(n) {
    this.value *= n;
    return this;
  }
  getResult() {
    return this.value;
  }
}
const calc = new Calculator(10);
console.log(calc.add(5).multiply(2).getResult()); // 30
30

Декоратор метода с помощью Proxy

С помощью Proxy можно перехватывать вызовы методов и добавлять логирование или проверки.

Пример
class Logger {
  log(message) {
    console.log(`[LOG]: ${message}`);
  }
}
const logger = new Logger();
const handler = {
  get(target, prop, receiver) {
    const original = Reflect.get(target, prop, receiver);
    if (typeof original === 'function') {
      return function (...args) {
        console.log(`Вызов метода ${prop} с аргументами:`, args);
        return original.apply(this, args);
      };
    }
    return original;
  }
};
const proxy = new Proxy(logger, handler);
proxy.log('Тестовое сообщение');
Вызов метода log с аргументами: ["Тестовое сообщение"]
[LOG]: Тестовое сообщение

Примесь с использованием Object.assign для методов

Методы из одного объекта можно скопировать в прототип класса.

Пример
const canEat = {
  eat() {
    return `${this.name} ест`;
  }
};
const canSleep = {
  sleep() {
    return `${this.name} спит`;
  }
};
class Animal {
  constructor(name) {
    this.name = name;
  }
}
Object.assign(Animal.prototype, canEat, canSleep);
const cat = new Animal('Кошка');
console.log(cat.eat()); // "Кошка ест"
console.log(cat.sleep()); // "Кошка спит"
Кошка ест
Кошка спит

Генераторы как методы класса

Метод может быть генератором (*).

Пример
class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }
  *[Symbol.iterator]() {
    for (let i = this.start; i <= this.end; i++) {
      yield i;
    }
  }
}
const range = new Range(1, 3);
console.log([...range]); // [1,2,3]
[1, 2, 3]

Статический метод как фабрика

Пример
class User {
  constructor(name, role) {
    this.name = name;
    this.role = role;
  }
  static createAdmin(name) {
    return new User(name, 'admin');
  }
}
const admin = User.createAdmin('Директор');
console.log(admin.role); // 'admin'
admin

Приватный статический метод

Комбинация static и # позволяет создавать приватные вспомогательные функции.

Пример
class MathUtils {
  static #factorial(n) {
    if (n <= 1) return 1;
    return n * this.#factorial(n - 1);
  }
  static factorial(n) {
    if (n < 0) throw new Error('Отрицательное число');
    return this.#factorial(n);
  }
}
console.log(MathUtils.factorial(5)); // 120
// console.log(MathUtils.#factorial(5)); // SyntaxError
120

Динамическое имя метода (computed property name)

Имя метода может быть вычислено во время объявления класса.

Пример
const methodName = 'greet';
class Person {
  constructor(name) {
    this.name = name;
  }
  [methodName]() {
    return `Здравствуйте, ${this.name}!`;
  }
}
const person = new Person('Ольга');
console.log(person.greet()); // 'Здравствуйте, Ольга!'
Здравствуйте, Ольга!

Эти примеры показывают гибкость методов классов и их применения в реальных проектах.

Методы классов в JavaScript - comments

En
Javascript class method (javascript)