← Назад к вопросам

Как ООП используется во Frontend?

2.3 Middle🔥 181 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как ООП используется во Frontend?

Объектно-ориентированное программирование (ООП) — это не просто парадигма, это способ организовать код так, чтобы он был переиспользуемым, тестируемым и поддерживаемым. На фронтенде ООП используется повсеместно, хотя это может быть не всегда очевидно.

ООП в JavaScript / TypeScript

Классы — основа ООП:

class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  greet() {
    return `Hello, I'm ${this.name}`;
  }

  changeEmail(newEmail) {
    this.email = newEmail;
  }
}

const user = new User('John', 'john@example.com');
console.log(user.greet()); // Hello, I'm John

Инкапсуляция — скрытие деталей реализации:

class BankAccount {
  #balance = 0;  // Private field

  constructor(initialBalance) {
    this.#balance = initialBalance;
  }

  deposit(amount) {
    if (amount > 0) {
      this.#balance += amount;
      return this.#balance;
    }
    throw new Error('Amount must be positive');
  }

  getBalance() {
    return this.#balance;
  }
}

const account = new BankAccount(1000);
account.deposit(500); // OK
console.log(account.#balance); // Error: private field

Наследование — переиспользование кода:

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a sound`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  speak() {
    console.log(`${this.name} barks`);
  }

  getInfo() {
    return `${this.name} is a ${this.breed}`;
  }
}

const dog = new Dog('Rex', 'Labrador');
dog.speak(); // Rex barks
console.log(dog.getInfo()); // Rex is a Labrador

Полиморфизм — один интерфейс, разные реализации:

class Shape {
  getArea() {
    throw new Error('Must implement getArea()');
  }
}

class Circle extends Shape {
  constructor(radius) {
    super();
    this.radius = radius;
  }

  getArea() {
    return Math.PI * this.radius ** 2;
  }
}

class Rectangle extends Shape {
  constructor(width, height) {
    super();
    this.width = width;
    this.height = height;
  }

  getArea() {
    return this.width * this.height;
  }
}

const shapes = [
  new Circle(5),
  new Rectangle(4, 6)
];

shapes.forEach(shape => {
  console.log(shape.getArea());
});

ООП в React компонентах

Классовые компоненты (Legacy, но все ещё используются):

class UserProfile extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      user: null,
      loading: true
    };
  }

  componentDidMount() {
    this.fetchUser();
  }

  fetchUser = async () => {
    try {
      const response = await fetch(`/api/users/${this.props.userId}`);
      const user = await response.json();
      this.setState({ user, loading: false });
    } catch (error) {
      console.error(error);
      this.setState({ loading: false });
    }
  }

  render() {
    const { user, loading } = this.state;

    if (loading) return <div>Loading...</div>;
    if (!user) return <div>User not found</div>;

    return (
      <div>
        <h1>{user.name}</h1>
        <p>{user.email}</p>
      </div>
    );
  }
}

Функциональные компоненты с хуками (современный подход):

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchUser = async () => {
      try {
        const response = await fetch(`/api/users/${userId}`);
        const data = await response.json();
        setUser(data);
      } finally {
        setLoading(false);
      }
    };

    fetchUser();
  }, [userId]);

  if (loading) return <div>Loading...</div>;
  if (!user) return <div>User not found</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

ООП паттерны на фронтенде

Паттерн Singleton — один экземпляр:

class Logger {
  static instance = null;

  static getInstance() {
    if (!Logger.instance) {
      Logger.instance = new Logger();
    }
    return Logger.instance;
  }

  log(message) {
    console.log(`[${new Date().toISOString()}] ${message}`);
  }
}

const logger1 = Logger.getInstance();
const logger2 = Logger.getInstance();
console.log(logger1 === logger2); // true — один и тот же объект

Паттерн Factory — создание объектов:

class Button {
  constructor(text, onClick) {
    this.text = text;
    this.onClick = onClick;
  }

  render() {
    return `<button>${this.text}</button>`;
  }
}

class ButtonFactory {
  static createPrimaryButton(text, onClick) {
    return new Button(text, onClick);
  }

  static createSecondaryButton(text, onClick) {
    return new Button(text, onClick);
  }
}

const submitBtn = ButtonFactory.createPrimaryButton('Submit', () => {});

Паттерн Observer — реактивность:

class EventEmitter {
  constructor() {
    this.events = {};
  }

  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }

  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(callback => callback(data));
    }
  }
}

const emitter = new EventEmitter();

emitter.on('userLoggedIn', (user) => {
  console.log('User logged in:', user);
});

emitter.emit('userLoggedIn', { name: 'John' });

Service Classes для бизнес-логики

class UserService {
  constructor(apiClient) {
    this.apiClient = apiClient;
  }

  async getUserById(id) {
    const user = await this.apiClient.get(`/users/${id}`);
    return this.mapToDomain(user);
  }

  async createUser(userData) {
    const validated = this.validateUserData(userData);
    return await this.apiClient.post('/users', validated);
  }

  async updateUser(id, userData) {
    const validated = this.validateUserData(userData);
    return await this.apiClient.put(`/users/${id}`, validated);
  }

  validateUserData(data) {
    if (!data.email || !data.email.includes('@')) {
      throw new Error('Invalid email');
    }
    if (!data.name || data.name.length < 2) {
      throw new Error('Invalid name');
    }
    return data;
  }

  mapToDomain(apiResponse) {
    return {
      id: apiResponse.id,
      name: apiResponse.full_name,
      email: apiResponse.email_address
    };
  }
}

// Использование
const userService = new UserService(apiClient);
const user = await userService.getUserById(123);

TypeScript для ООП

interface IUser {
  id: number;
  name: string;
  email: string;
}

class UserRepository {
  async findById(id: number): Promise<IUser | null> {
    // Реализация
    return null;
  }

  async save(user: IUser): Promise<void> {
    // Реализация
  }
}

class UserController {
  constructor(private userRepository: UserRepository) {}

  async getUser(id: number): Promise<IUser | null> {
    return this.userRepository.findById(id);
  }

  async createUser(userData: Partial<IUser>): Promise<IUser> {
    const user: IUser = {
      id: Date.now(),
      name: userData.name || '',
      email: userData.email || ''
    };
    await this.userRepository.save(user);
    return user;
  }
}

Принципы SOLID во Frontend

S — Single Responsibility

// Плохо: класс делает слишком много
class UserManager {
  fetchUser() { /* ... */ }
  validateEmail() { /* ... */ }
  sendEmail() { /* ... */ }
  renderUI() { /* ... */ }
}

// Хорошо: разделение ответственности
class UserService { fetchUser() { /* ... */ } }
class EmailValidator { validateEmail() { /* ... */ } }
class EmailSender { sendEmail() { /* ... */ } }
class UserComponent { renderUI() { /* ... */ } }

D — Dependency Injection

class UserComponent {
  constructor(
    private userService: UserService,
    private logger: Logger
  ) {}

  async loadUser(id: number) {
    this.logger.log('Loading user');
    const user = await this.userService.getUser(id);
    return user;
  }
}

// Внедрение зависимостей
const userService = new UserService();
const logger = new Logger();
const component = new UserComponent(userService, logger);

Абстрактные классы

abstract class APIClient {
  abstract request(endpoint: string, options?: any): Promise<any>;

  async get(endpoint: string) {
    return this.request(endpoint, { method: 'GET' });
  }

  async post(endpoint: string, data: any) {
    return this.request(endpoint, {
      method: 'POST',
      body: JSON.stringify(data)
    });
  }
}

class FetchAPIClient extends APIClient {
  async request(endpoint: string, options: any = {}) {
    const response = await fetch(endpoint, options);
    return response.json();
  }
}

Заключение

ООП на фронтенде используется для:

  1. Организации кода — классы и модули
  2. Переиспользования — наследование и композиция
  3. Тестируемости — внедрение зависимостей
  4. Масштабируемости — SOLID принципы
  5. Типизации — TypeScript interfaces и классы

Модерный фронтенд сочетает ООП с функциональным программированием (React хуки, FP функции), создавая мощный и гибкий код.