Методы классов 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()); // 'Здравствуйте, Ольга!'
Здравствуйте, Ольга!
Эти примеры показывают гибкость методов классов и их применения в реальных проектах.